diff --git a/Languages/IronPython/IronPython/Runtime/Binding/MetaPythonFunction.cs b/Languages/IronPython/IronPython/Runtime/Binding/MetaPythonFunction.cs index a696e72e3d..70509f52b2 100644 --- a/Languages/IronPython/IronPython/Runtime/Binding/MetaPythonFunction.cs +++ b/Languages/IronPython/IronPython/Runtime/Binding/MetaPythonFunction.cs @@ -1,1143 +1,1154 @@ -/* **************************************************************************** - * - * Copyright (c) Microsoft Corporation. - * - * This source code is subject to terms and conditions of the Apache License, Version 2.0. A - * copy of the license can be found in the License.html file at the root of this distribution. If - * you cannot locate the Apache License, Version 2.0, please send an email to - * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound - * by the terms of the Apache License, Version 2.0. - * - * You must not remove this notice, or any other, from this software. - * - * - * ***************************************************************************/ - -#if !CLR2 -using System.Linq.Expressions; -#else -using Microsoft.Scripting.Ast; -#endif - -using System; -using System.Collections; -using System.Collections.Generic; -using System.Diagnostics; -using System.Reflection; -using System.Dynamic; - -using Microsoft.Scripting; -using Microsoft.Scripting.Actions; -using Microsoft.Scripting.Actions.Calls; -using Microsoft.Scripting.Runtime; -using Microsoft.Scripting.Utils; - -using IronPython.Runtime.Operations; -using IronPython.Runtime.Types; - -namespace IronPython.Runtime.Binding { - using Ast = Expression; - using AstUtils = Microsoft.Scripting.Ast.Utils; - - class MetaPythonFunction : MetaPythonObject, IPythonInvokable, IPythonOperable, IPythonConvertible, IInferableInvokable, IConvertibleMetaObject, IPythonGetable { - public MetaPythonFunction(Expression/*!*/ expression, BindingRestrictions/*!*/ restrictions, PythonFunction/*!*/ value) - : base(expression, BindingRestrictions.Empty, value) { - Assert.NotNull(value); - } - - #region IPythonInvokable Members - - public DynamicMetaObject/*!*/ Invoke(PythonInvokeBinder/*!*/ pythonInvoke, Expression/*!*/ codeContext, DynamicMetaObject/*!*/ target, DynamicMetaObject/*!*/[]/*!*/ args) { - return new FunctionBinderHelper(pythonInvoke, this, codeContext, args).MakeMetaObject(); - } - - #endregion - - - #region IPythonGetable Members - - public DynamicMetaObject GetMember(PythonGetMemberBinder member, DynamicMetaObject codeContext) { - return BindGetMemberWorker(member, member.Name, codeContext); - } - - #endregion - - #region MetaObject Overrides - - public override DynamicMetaObject/*!*/ BindInvokeMember(InvokeMemberBinder/*!*/ action, DynamicMetaObject/*!*/[]/*!*/ args) { - ParameterExpression tmp = Expression.Parameter(typeof(object)); - - // first get the default binder value - DynamicMetaObject fallback = action.FallbackInvokeMember(this, args); - - // then fallback w/ an error suggestion that does a late bound lookup. - return action.FallbackInvokeMember( - this, - args, - new DynamicMetaObject( - Ast.Block( - new[] { tmp }, - Ast.Condition( - Ast.NotEqual( - Ast.Assign( - tmp, - Ast.Call( - typeof(PythonOps).GetMethod("PythonFunctionGetMember"), - AstUtils.Convert( - Expression, - typeof(PythonFunction) - ), - Expression.Constant(action.Name) - ) - ), - Ast.Constant(OperationFailed.Value) - ), - action.FallbackInvoke( - new DynamicMetaObject(tmp, BindingRestrictions.Empty), - args, - null - ).Expression, - AstUtils.Convert(fallback.Expression, typeof(object)) - ) - ), - BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)).Merge(fallback.Restrictions) - ) - ); - } - - public override DynamicMetaObject/*!*/ BindInvoke(InvokeBinder/*!*/ call, params DynamicMetaObject/*!*/[]/*!*/ args) { - return new FunctionBinderHelper(call, this, null, args).MakeMetaObject(); - } - - public override DynamicMetaObject BindConvert(ConvertBinder/*!*/ conversion) { - return ConvertWorker(conversion, conversion.Type, conversion.Explicit ? ConversionResultKind.ExplicitCast : ConversionResultKind.ImplicitCast); - } - - public DynamicMetaObject BindConvert(PythonConversionBinder binder) { - return ConvertWorker(binder, binder.Type, binder.ResultKind); - } - - public DynamicMetaObject ConvertWorker(DynamicMetaObjectBinder binder, Type type, ConversionResultKind kind) { - if (type.IsSubclassOf(typeof(Delegate))) { - return MakeDelegateTarget(binder, type, Restrict(typeof(PythonFunction))); - } - return FallbackConvert(binder); - } - - public override System.Collections.Generic.IEnumerable GetDynamicMemberNames() { - foreach (object o in Value.__dict__.Keys) { - if (o is string) { - yield return (string)o; - } - } - } - - public override DynamicMetaObject BindGetMember(GetMemberBinder binder) { - return BindGetMemberWorker(binder, binder.Name, PythonContext.GetCodeContextMO(binder)); - } - - private DynamicMetaObject BindGetMemberWorker(DynamicMetaObjectBinder binder, string name, DynamicMetaObject codeContext) { - ParameterExpression tmp = Expression.Parameter(typeof(object)); - - // first get the default binder value - DynamicMetaObject fallback = FallbackGetMember(binder, this, codeContext); - - // then fallback w/ an error suggestion that does a late bound lookup. - return FallbackGetMember( - binder, - this, - codeContext, - new DynamicMetaObject( - Ast.Block( - new[] { tmp }, - Ast.Condition( - Ast.NotEqual( - Ast.Assign( - tmp, - Ast.Call( - typeof(PythonOps).GetMethod("PythonFunctionGetMember"), - AstUtils.Convert( - Expression, - typeof(PythonFunction) - ), - Expression.Constant(name) - ) - ), - Ast.Constant(OperationFailed.Value) - ), - tmp, - AstUtils.Convert(fallback.Expression, typeof(object)) - ) - ), - BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)).Merge(fallback.Restrictions) - ) - ); - } - - private static DynamicMetaObject FallbackGetMember(DynamicMetaObjectBinder binder, DynamicMetaObject self, DynamicMetaObject codeContext) { - return FallbackGetMember(binder, self, codeContext, null); - } - - private static DynamicMetaObject FallbackGetMember(DynamicMetaObjectBinder binder, DynamicMetaObject self, DynamicMetaObject codeContext, DynamicMetaObject errorSuggestion) { - PythonGetMemberBinder pyGetMem = binder as PythonGetMemberBinder; - if (pyGetMem != null) { - return pyGetMem.Fallback(self, codeContext, errorSuggestion); - } - - return ((GetMemberBinder)binder).FallbackGetMember(self, errorSuggestion); - } - - public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) { - ParameterExpression tmp = Expression.Parameter(typeof(bool)); - - // fallback w/ an error suggestion that does a late bound set - return binder.FallbackSetMember( - this, - value, - new DynamicMetaObject( - Ast.Call( - typeof(PythonOps).GetMethod("PythonFunctionSetMember"), - AstUtils.Convert( - Expression, - typeof(PythonFunction) - ), - Expression.Constant(binder.Name), - AstUtils.Convert( - value.Expression, - typeof(object) - ) - ), - BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)) - ) - ); - } - - public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) { - switch (binder.Name) { - case "func_dict": - case "__dict__": - return new DynamicMetaObject( - Expression.Call( - typeof(PythonOps).GetMethod("PythonFunctionDeleteDict") - ), - BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)) - ); - case "__doc__": - case "func_doc": - return new DynamicMetaObject( - Expression.Call( - typeof(PythonOps).GetMethod("PythonFunctionDeleteDoc"), - Expression.Convert(Expression, typeof(PythonFunction)) - ), - BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)) - ); - case "func_defaults": - return new DynamicMetaObject( - Expression.Call( - typeof(PythonOps).GetMethod("PythonFunctionDeleteDefaults"), - Expression.Convert(Expression, typeof(PythonFunction)) - ), - BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)) - ); - } - - // first get the default binder value - DynamicMetaObject fallback = binder.FallbackDeleteMember(this); - - // then fallback w/ an error suggestion that does a late bound delete - return binder.FallbackDeleteMember( - this, - new DynamicMetaObject( - Expression.Condition( - Ast.Call( - typeof(PythonOps).GetMethod("PythonFunctionDeleteMember"), - AstUtils.Convert( - Expression, - typeof(PythonFunction) - ), - Expression.Constant(binder.Name) - ), - Expression.Default(typeof(void)), // we deleted the member - AstUtils.Convert( - fallback.Expression, // report language specific error, - typeof(void) - ) - ), - BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)).Merge(fallback.Restrictions) - ) - ); - } - - #endregion - - #region Calls - - /// - /// Performs the actual work of binding to the function. - /// - /// Overall this works by going through the arguments and attempting to bind all the outstanding known - /// arguments - position arguments and named arguments which map to parameters are easy and handled - /// in the 1st pass for GetArgumentsForRule. We also pick up any extra named or position arguments which - /// will need to be passed off to a kw argument or a params array. - /// - /// After all the normal args have been assigned to do a 2nd pass in FinishArguments. Here we assign - /// a value to either a value from the params list, kw-dict, or defaults. If there is ambiguity between - /// this (e.g. we have a splatted params list, kw-dict, and defaults) we call a helper which extracts them - /// in the proper order (first try the list, then the dict, then the defaults). - /// - class FunctionBinderHelper { - private readonly MetaPythonFunction/*!*/ _func; // the meta object for the function we're calling - private readonly DynamicMetaObject/*!*/[]/*!*/ _args; // the arguments for the function - private readonly DynamicMetaObject/*!*/[]/*!*/ _originalArgs; // the original arguments for the function - private readonly DynamicMetaObjectBinder/*!*/ _call; // the signature for the method call - private readonly Expression _codeContext; // the code context expression if one is available. - - private List/*!*/ _temps; // temporary variables allocated to create the rule - private ParameterExpression _dict, _params, _paramsLen; // splatted dictionary & params + the initial length of the params array, null if not provided. - private List _init; // a set of initialization code (e.g. creating a list for the params array) - private Expression _error; // a custom error expression if the default needs to be overridden. - private bool _extractedParams; // true if we needed to extract a parameter from the parameter list. - private bool _extractedKeyword; // true if we needed to extract a parameter from the kw list. - private Expression _deferTest; // non-null if we have a test which could fail at runtime and we need to fallback to deferal - private Expression _userProvidedParams; // expression the user provided that should be expanded for params. - private Expression _paramlessCheck; // tests when we have no parameters - - public FunctionBinderHelper(DynamicMetaObjectBinder/*!*/ call, MetaPythonFunction/*!*/ function, Expression codeContext, DynamicMetaObject/*!*/[]/*!*/ args) { - PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "PythonFunction Invoke " + function.Value.FunctionCompatibility + " w/ " + args.Length + " args"); - PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, "PythonFunction"); - - _call = call; - _func = function; - _args = args; - _originalArgs = args; - _temps = new List(); - _codeContext = codeContext; - - // Remove the passed in instance argument if present - int instanceIndex = Signature.IndexOf(ArgumentType.Instance); - if (instanceIndex > -1) { - _args = ArrayUtils.RemoveAt(_args, instanceIndex); - } - } - - public DynamicMetaObject/*!*/ MakeMetaObject() { - Expression[] invokeArgs = GetArgumentsForRule(); - BindingRestrictions restrict = _func.Restrictions.Merge(GetRestrictions().Merge(BindingRestrictions.Combine(_args))); - DynamicMetaObject res; - - if (invokeArgs != null) { - // successful call - Expression target = AddInitialization(MakeFunctionInvoke(invokeArgs)); - - if (_temps.Count > 0) { - target = Ast.Block( - _temps, - target - ); - } - - res = new DynamicMetaObject( - target, - restrict - ); - } else if (_error != null) { - // custom error generated while figuring out the call - res = new DynamicMetaObject(_error, restrict); - } else { - // generic error - res = new DynamicMetaObject( - _call.Throw( - Ast.Call( - typeof(PythonOps).GetMethod(Signature.HasKeywordArgument() ? "BadKeywordArgumentError" : "FunctionBadArgumentError"), - AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), - AstUtils.Constant(Signature.GetProvidedPositionalArgumentCount()) - ), - typeof(object) - ), - restrict - ); - } - - DynamicMetaObject[] deferArgs = ArrayUtils.Insert(_func, _originalArgs); - - if (_codeContext != null) { - deferArgs = ArrayUtils.Insert(new DynamicMetaObject(_codeContext, BindingRestrictions.Empty), deferArgs); - } - - return BindingHelpers.AddDynamicTestAndDefer( - _call, - res, - deferArgs, - new ValidationInfo(_deferTest), - res.Expression.Type // force defer to our return type, our restrictions guarantee this to be true (only defaults can change, and we restrict to the delegate type) - ); - } - - - private CallSignature Signature { - get { - return BindingHelpers.GetCallSignature(_call); - } - } - - /// - /// Makes the test for our rule. - /// - private BindingRestrictions/*!*/ GetRestrictions() { - if (!Signature.HasKeywordArgument()) { - return GetSimpleRestriction(); - } - - return GetComplexRestriction(); - } - - /// - /// Makes the test when we just have simple positional arguments. - /// - private BindingRestrictions/*!*/ GetSimpleRestriction() { - _deferTest = Ast.Equal( - Ast.Call( - typeof(PythonOps).GetMethod("FunctionGetCompatibility"), - Ast.Convert(_func.Expression, typeof(PythonFunction)) - ), - AstUtils.Constant(_func.Value.FunctionCompatibility) - ); - - return BindingRestrictionsHelpers.GetRuntimeTypeRestriction( - _func.Expression, typeof(PythonFunction) - ); - } - - /// - /// Makes the test when we have a keyword argument call or splatting. - /// - /// - private BindingRestrictions/*!*/ GetComplexRestriction() { - if (_extractedKeyword) { - return BindingRestrictions.GetInstanceRestriction(_func.Expression, _func.Value); - } - - return GetSimpleRestriction(); - } - - /// - /// Gets the array of expressions which correspond to each argument for the function. These - /// correspond with the function as it's defined in Python and must be transformed for our - /// delegate type before being used. - /// - private Expression/*!*/[]/*!*/ GetArgumentsForRule() { - Expression[] exprArgs = new Expression[_func.Value.NormalArgumentCount + _func.Value.ExtraArguments]; - List extraArgs = null; - Dictionary namedArgs = null; - int instanceIndex = Signature.IndexOf(ArgumentType.Instance); - - // walk all the provided args and find out where they go... - for (int i = 0; i < _args.Length; i++) { - int parameterIndex = (instanceIndex == -1 || i < instanceIndex) ? i : i + 1; - - switch (Signature.GetArgumentKind(i)) { - case ArgumentType.Dictionary: - _args[parameterIndex] = MakeDictionaryCopy(_args[parameterIndex]); - continue; - - case ArgumentType.List: - _userProvidedParams = _args[parameterIndex].Expression; - continue; - - case ArgumentType.Named: - _extractedKeyword = true; - bool foundName = false; - for (int j = 0; j < _func.Value.NormalArgumentCount; j++) { - if (_func.Value.ArgNames[j] == Signature.GetArgumentName(i)) { - if (exprArgs[j] != null) { - // kw-argument provided for already provided normal argument. - if (_error == null) { - _error = _call.Throw( - Expression.Call( - typeof(PythonOps).GetMethod("MultipleKeywordArgumentError"), - GetFunctionParam(), - Expression.Constant(_func.Value.ArgNames[j]) - ), - typeof(object) - ); - } - return null; - } - - exprArgs[j] = _args[parameterIndex].Expression; - foundName = true; - break; - } - } - - if (!foundName) { - if (namedArgs == null) { - namedArgs = new Dictionary(); - } - namedArgs[Signature.GetArgumentName(i)] = _args[parameterIndex].Expression; - } - continue; - } - - if (i < _func.Value.NormalArgumentCount) { - exprArgs[i] = _args[parameterIndex].Expression; - } else { - if (extraArgs == null) { - extraArgs = new List(); - } - extraArgs.Add(_args[parameterIndex].Expression); - } - } - - if (!FinishArguments(exprArgs, extraArgs, namedArgs)) { - if (namedArgs != null && _func.Value.ExpandDictPosition == -1) { - MakeUnexpectedKeywordError(namedArgs); - } - - return null; - } - - return GetArgumentsForTargetType(exprArgs); - } - - /// - /// Binds any missing arguments to values from params array, kw dictionary, or default values. - /// - private bool FinishArguments(Expression[] exprArgs, List paramsArgs, Dictionary namedArgs) { - int noDefaults = _func.Value.NormalArgumentCount - _func.Value.Defaults.Length; // number of args w/o defaults - - for (int i = 0; i < _func.Value.NormalArgumentCount; i++) { - if (exprArgs[i] != null) { - if (_userProvidedParams != null && i >= Signature.GetProvidedPositionalArgumentCount()) { - exprArgs[i] = ValidateNotDuplicate(exprArgs[i], _func.Value.ArgNames[i], i); - } - continue; - } - - if (i < noDefaults) { - exprArgs[i] = ExtractNonDefaultValue(_func.Value.ArgNames[i]); - if (exprArgs[i] == null) { - // can't get a value, this is an invalid call. - return false; - } - } else { - exprArgs[i] = ExtractDefaultValue(i, i - noDefaults); - } - } - - if (!TryFinishList(exprArgs, paramsArgs) || - !TryFinishDictionary(exprArgs, namedArgs)) - return false; - - // add check for extra parameters. - AddCheckForNoExtraParameters(exprArgs); - - return true; - } - - /// - /// Creates the argument for the list expansion parameter. - /// - private bool TryFinishList(Expression[] exprArgs, List paramsArgs) { - if (_func.Value.ExpandListPosition != -1) { - if (_userProvidedParams != null) { - if (_params == null && paramsArgs == null) { - // we didn't extract any params, we can re-use a Tuple or - // make a single copy. - exprArgs[_func.Value.ExpandListPosition] = Ast.Call( - typeof(PythonOps).GetMethod("GetOrCopyParamsTuple"), - GetFunctionParam(), - AstUtils.Convert(_userProvidedParams, typeof(object)) - ); - } else { - // user provided a sequence to be expanded, and we may have used it, - // or we have extra args. - EnsureParams(); - - exprArgs[_func.Value.ExpandListPosition] = Ast.Call( - typeof(PythonOps).GetMethod("MakeTupleFromSequence"), - AstUtils.Convert(_params, typeof(object)) - ); - - if (paramsArgs != null) { - MakeParamsAddition(paramsArgs); - } - } - } else { - exprArgs[_func.Value.ExpandListPosition] = MakeParamsTuple(paramsArgs); - } - } else if (paramsArgs != null) { - // extra position args which are unused and no where to put them. - return false; - } - return true; - } - - /// - /// Adds extra positional arguments to the start of the expanded list. - /// - private void MakeParamsAddition(List paramsArgs) { - _extractedParams = true; - - List args = new List(paramsArgs.Count + 1); - args.Add(_params); - args.AddRange(paramsArgs); - - EnsureInit(); - - _init.Add( - AstUtils.ComplexCallHelper( - typeof(PythonOps).GetMethod("AddParamsArguments"), - args.ToArray() - ) - ); - } - - /// - /// Creates the argument for the dictionary expansion parameter. - /// - private bool TryFinishDictionary(Expression[] exprArgs, Dictionary namedArgs) { - if (_func.Value.ExpandDictPosition != -1) { - if (_dict != null) { - // used provided a dictionary to be expanded - exprArgs[_func.Value.ExpandDictPosition] = _dict; - if (namedArgs != null) { - foreach (KeyValuePair kvp in namedArgs) { - MakeDictionaryAddition(kvp); - } - } - } else { - exprArgs[_func.Value.ExpandDictPosition] = MakeDictionary(namedArgs); - } - } else if (namedArgs != null) { - // extra named args which are unused and no where to put them. - return false; - } - return true; - } - - /// - /// Adds an unbound keyword argument into the dictionary. - /// - /// - private void MakeDictionaryAddition(KeyValuePair kvp) { - _init.Add( - Ast.Call( - typeof(PythonOps).GetMethod("AddDictionaryArgument"), - AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), - AstUtils.Constant(kvp.Key), - AstUtils.Convert(kvp.Value, typeof(object)), - AstUtils.Convert(_dict, typeof(PythonDictionary)) - ) - ); - } - - /// - /// Adds a check to the last parameter (so it's evaluated after we've extracted - /// all the parameters) to ensure that we don't have any extra params or kw-params - /// when we don't have a params array or params dict to expand them into. - /// - private void AddCheckForNoExtraParameters(Expression[] exprArgs) { - List tests = new List(3); - - // test we've used all of the extra parameters - if (_func.Value.ExpandListPosition == -1) { - if (_params != null) { - // we used some params, they should have gone down to zero... - tests.Add( - Ast.Call( - typeof(PythonOps).GetMethod("CheckParamsZero"), - AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), - _params - ) - ); - } else if (_userProvidedParams != null) { - // the user provided params, we didn't need any, and they should be zero - tests.Add( - Ast.Call( - typeof(PythonOps).GetMethod("CheckUserParamsZero"), - AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), - AstUtils.Convert(_userProvidedParams, typeof(object)) - ) - ); - } - } - - // test that we've used all the extra named arguments - if (_func.Value.ExpandDictPosition == -1 && _dict != null) { - tests.Add( - Ast.Call( - typeof(PythonOps).GetMethod("CheckDictionaryZero"), - AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), - AstUtils.Convert(_dict, typeof(IDictionary)) - ) - ); - } - - if (tests.Count != 0) { - if (exprArgs.Length != 0) { - // if we have arguments run the tests after the last arg is evaluated. - Expression last = exprArgs[exprArgs.Length - 1]; - - ParameterExpression temp; - - _temps.Add(temp = Ast.Variable(last.Type, "$temp")); - - tests.Insert(0, Ast.Assign(temp, last)); - tests.Add(temp); - exprArgs[exprArgs.Length - 1] = Ast.Block(tests.ToArray()); - } else { - // otherwise run them right before the method call - _paramlessCheck = Ast.Block(tests.ToArray()); - } - } - } - - /// - /// Helper function to validate that a named arg isn't duplicated with by - /// a params list or the dictionary (or both). - /// - private Expression ValidateNotDuplicate(Expression value, string name, int position) { - EnsureParams(); - - return Ast.Block( - Ast.Call( - typeof(PythonOps).GetMethod("VerifyUnduplicatedByPosition"), - AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), // function - AstUtils.Constant(name, typeof(string)), // name - AstUtils.Constant(position), // position - _paramsLen // params list length - ), - value - ); - } - - /// - /// Helper function to get a value (which has no default) from either the - /// params list or the dictionary (or both). - /// - private Expression ExtractNonDefaultValue(string name) { - if (_userProvidedParams != null) { - // expanded params - if (_dict != null) { - // expanded params & dict - return ExtractFromListOrDictionary(name); - } else { - return ExtractNextParamsArg(); - } - } else if (_dict != null) { - // expanded dict - return ExtractDictionaryArgument(name); - } - - // missing argument, no default, no expanded params or dict. - return null; - } - - /// - /// Helper function to get the specified variable from the dictionary. - /// - private Expression ExtractDictionaryArgument(string name) { - _extractedKeyword = true; - - return Ast.Call( - typeof(PythonOps).GetMethod("ExtractDictionaryArgument"), - AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), // function - AstUtils.Constant(name, typeof(string)), // name - AstUtils.Constant(Signature.ArgumentCount), // arg count - AstUtils.Convert(_dict, typeof(PythonDictionary)) // dictionary - ); - } - - /// - /// Helper function to extract the variable from defaults, or to call a helper - /// to check params / kw-dict / defaults to see which one contains the actual value. - /// - private Expression ExtractDefaultValue(int index, int dfltIndex) { - if (_dict == null && _userProvidedParams == null) { - // we can pull the default directly - return Ast.Call( - typeof(PythonOps).GetMethod("FunctionGetDefaultValue"), - AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), - AstUtils.Constant(dfltIndex) - ); - } else { - // we might have a conflict, check the default last. - if (_userProvidedParams != null) { - EnsureParams(); - } - _extractedKeyword = true; - return Ast.Call( - typeof(PythonOps).GetMethod("GetFunctionParameterValue"), - AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), - AstUtils.Constant(dfltIndex), - AstUtils.Constant(_func.Value.ArgNames[index], typeof(string)), - VariableOrNull(_params, typeof(List)), - VariableOrNull(_dict, typeof(PythonDictionary)) - ); - } - } - - /// - /// Helper function to extract from the params list or dictionary depending upon - /// which one has an available value. - /// - private Expression ExtractFromListOrDictionary(string name) { - EnsureParams(); - - _extractedKeyword = true; - - return Ast.Call( - typeof(PythonOps).GetMethod("ExtractAnyArgument"), - AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), // function - AstUtils.Constant(name, typeof(string)), // name - _paramsLen, // arg count - _params, // params list - AstUtils.Convert(_dict, typeof(IDictionary)) // dictionary - ); - } - - private void EnsureParams() { - if (!_extractedParams) { - Debug.Assert(_userProvidedParams != null); - MakeParamsCopy(_userProvidedParams); - _extractedParams = true; - } - } - - /// - /// Helper function to extract the next argument from the params list. - /// - private Expression ExtractNextParamsArg() { - if (!_extractedParams) { - MakeParamsCopy(_userProvidedParams); - - _extractedParams = true; - } - - return Ast.Call( - typeof(PythonOps).GetMethod("ExtractParamsArgument"), - AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), // function - AstUtils.Constant(Signature.ArgumentCount), // arg count - _params // list - ); - } - - private static Expression VariableOrNull(ParameterExpression var, Type type) { - if (var != null) { - return AstUtils.Convert( - var, - type - ); - } - return AstUtils.Constant(null, type); - } - - /// - /// Fixes up the argument list for the appropriate target delegate type. - /// - private Expression/*!*/[]/*!*/ GetArgumentsForTargetType(Expression[] exprArgs) { - Type target = _func.Value.func_code.Target.GetType(); - if (target == typeof(Func)) { - exprArgs = new Expression[] { - AstUtils.NewArrayHelper(typeof(object), exprArgs) - }; - } - - return exprArgs; - } - - /// - /// Helper function to get the function argument strongly typed. - /// - private UnaryExpression/*!*/ GetFunctionParam() { - return Ast.Convert(_func.Expression, typeof(PythonFunction)); - } - - /// - /// Called when the user is expanding a dictionary - we copy the user - /// dictionary and verify that it contains only valid string names. - /// - private DynamicMetaObject/*!*/ MakeDictionaryCopy(DynamicMetaObject/*!*/ userDict) { - Debug.Assert(_dict == null); - - userDict = userDict.Restrict(userDict.GetLimitType()); - _temps.Add(_dict = Ast.Variable(typeof(PythonDictionary), "$dict")); - - EnsureInit(); - - string methodName; - if (typeof(PythonDictionary).IsAssignableFrom(userDict.GetLimitType())) { - methodName = "CopyAndVerifyPythonDictionary"; - } else if (typeof(IDictionary).IsAssignableFrom(userDict.GetLimitType())) { - methodName = "CopyAndVerifyDictionary"; - } else { - methodName = "CopyAndVerifyUserMapping"; - } - - _init.Add( - Ast.Assign( - _dict, - Ast.Call( - typeof(PythonOps).GetMethod(methodName), - GetFunctionParam(), - AstUtils.Convert(userDict.Expression, userDict.GetLimitType()) - ) - ) - ); - return userDict; - } - - /// - /// Called when the user is expanding a params argument - /// - private void MakeParamsCopy(Expression/*!*/ userList) { - Debug.Assert(_params == null); - - _temps.Add(_params = Ast.Variable(typeof(List), "$list")); - _temps.Add(_paramsLen = Ast.Variable(typeof(int), "$paramsLen")); - - EnsureInit(); - - _init.Add( - Ast.Assign( - _params, - Ast.Call( - typeof(PythonOps).GetMethod("CopyAndVerifyParamsList"), - AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), - AstUtils.Convert(userList, typeof(object)) - ) - ) - ); - - _init.Add( - Ast.Assign(_paramsLen, - Ast.Add( - Ast.Call(_params, typeof(List).GetMethod("__len__")), - AstUtils.Constant(Signature.GetProvidedPositionalArgumentCount()) - ) - ) - ); - } - - /// - /// Called when the user hasn't supplied a dictionary to be expanded but the - /// function takes a dictionary to be expanded. - /// - private Expression MakeDictionary(Dictionary namedArgs) { - Debug.Assert(_dict == null); - _temps.Add(_dict = Ast.Variable(typeof(PythonDictionary), "$dict")); - - Expression dictCreator; - ParameterExpression dictRef = _dict; - - if (namedArgs != null) { - Debug.Assert(namedArgs.Count > 0); - - Expression[] items = new Expression[namedArgs.Count * 2]; - int itemIndex = 0; - foreach (KeyValuePair kvp in namedArgs) { - items[itemIndex++] = AstUtils.Convert(kvp.Value, typeof(object)); - items[itemIndex++] = AstUtils.Constant(kvp.Key, typeof(object)); - } - - dictCreator = Ast.Assign( - _dict, - Ast.Call( - typeof(PythonOps).GetMethod("MakeHomogeneousDictFromItems"), - Ast.NewArrayInit(typeof(object), items) - ) - ); - - } else { - dictCreator = Ast.Assign( - _dict, - Ast.Call( - typeof(PythonOps).GetMethod("MakeDict"), - AstUtils.Constant(0) - ) - ); - } - - return dictCreator; - } - - /// - /// Helper function to create the expression for creating the actual tuple passed through. - /// - private static Expression/*!*/ MakeParamsTuple(List extraArgs) { - if (extraArgs != null) { - return AstUtils.ComplexCallHelper( - typeof(PythonOps).GetMethod("MakeTuple"), - extraArgs.ToArray() - ); - } - return Ast.Call( - typeof(PythonOps).GetMethod("MakeTuple"), - Ast.NewArrayInit(typeof(object[])) - ); - } - - /// - /// Creates the code to invoke the target delegate function w/ the specified arguments. - /// - private Expression/*!*/ MakeFunctionInvoke(Expression[] invokeArgs) { - Type targetType = _func.Value.func_code.Target.GetType(); - MethodInfo method = targetType.GetMethod("Invoke"); - - // If calling generator, create the instance of PythonGenerator first - // and add it into the list of arguments - invokeArgs = ArrayUtils.Insert(GetFunctionParam(), invokeArgs); - - Expression invoke = AstUtils.SimpleCallHelper( - Ast.Convert( - Ast.Call( - _call.SupportsLightThrow() ? - typeof(PythonOps).GetMethod("FunctionGetLightThrowTarget") - : typeof(PythonOps).GetMethod("FunctionGetTarget"), - GetFunctionParam() - ), - targetType - ), - method, - invokeArgs - ); - - if (_paramlessCheck != null) { - invoke = Expression.Block(_paramlessCheck, invoke); - } - - return invoke; - } - - /// - /// Appends the initialization code for the call to the function if any exists. - /// - private Expression/*!*/ AddInitialization(Expression body) { - if (_init == null) return body; - - List res = new List(_init); - res.Add(body); - return Ast.Block(res); - } - - private void MakeUnexpectedKeywordError(Dictionary namedArgs) { - string name = null; - foreach (string id in namedArgs.Keys) { - name = id; - break; - } - - _error = _call.Throw( - Ast.Call( - typeof(PythonOps).GetMethod("UnexpectedKeywordArgumentError"), - AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), - AstUtils.Constant(name, typeof(string)) - ), - typeof(PythonOps) - ); - } - - private void EnsureInit() { - if (_init == null) _init = new List(); - } - } - - #endregion - - #region Operations - - private static DynamicMetaObject/*!*/ MakeCallSignatureRule(DynamicMetaObject self) { - return new DynamicMetaObject( - Ast.Call( - typeof(PythonOps).GetMethod("GetFunctionSignature"), - AstUtils.Convert( - self.Expression, - typeof(PythonFunction) - ) - ), - BindingRestrictionsHelpers.GetRuntimeTypeRestriction(self.Expression, typeof(PythonFunction)) - ); - } - - private static DynamicMetaObject MakeIsCallableRule(DynamicMetaObject/*!*/ self) { - return new DynamicMetaObject( - AstUtils.Constant(true), - BindingRestrictionsHelpers.GetRuntimeTypeRestriction(self.Expression, typeof(PythonFunction)) - ); - } - - #endregion - - #region Helpers - - public new PythonFunction/*!*/ Value { - get { - return (PythonFunction)base.Value; - } - } - - #endregion - - #region IPythonOperable Members - - DynamicMetaObject IPythonOperable.BindOperation(PythonOperationBinder action, DynamicMetaObject[] args) { - switch (action.Operation) { - case PythonOperationKind.CallSignatures: - return MakeCallSignatureRule(this); - case PythonOperationKind.IsCallable: - return MakeIsCallableRule(this); - } - - return null; - } - - #endregion - - #region IInvokableInferable Members - - InferenceResult IInferableInvokable.GetInferredType(Type delegateType, Type parameterType) { - if (!delegateType.IsSubclassOf(typeof(Delegate))) { - throw new InvalidOperationException(); - } - - MethodInfo invoke = delegateType.GetMethod("Invoke"); - ParameterInfo[] pis = invoke.GetParameters(); - if (pis.Length == Value.NormalArgumentCount) { - // our signatures are compatible - return new InferenceResult( - typeof(object), - Restrictions.Merge( - BindingRestrictions.GetTypeRestriction( - Expression, - typeof(PythonFunction) - ).Merge( - BindingRestrictions.GetExpressionRestriction( - Expression.Equal( - Expression.Call( - typeof(PythonOps).GetMethod("FunctionGetCompatibility"), - Expression.Convert(Expression, typeof(PythonFunction)) - ), - Expression.Constant(Value.FunctionCompatibility) - ) - ) - ) - ) - ); - - } - - return null; - } - - #endregion - - #region IConvertibleMetaObject Members - - bool IConvertibleMetaObject.CanConvertTo(Type/*!*/ type, bool @explicit) { - return type.IsSubclassOf(typeof(Delegate)); - } - - #endregion - } -} +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * dlr@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * + * ***************************************************************************/ + +#if !CLR2 +using System.Linq.Expressions; +#else +using Microsoft.Scripting.Ast; +#endif + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Reflection; +using System.Dynamic; + +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Actions.Calls; +using Microsoft.Scripting.Runtime; +using Microsoft.Scripting.Utils; + +using IronPython.Runtime.Operations; +using IronPython.Runtime.Types; + +namespace IronPython.Runtime.Binding { + using Ast = Expression; + using AstUtils = Microsoft.Scripting.Ast.Utils; + + class MetaPythonFunction : MetaPythonObject, IPythonInvokable, IPythonOperable, IPythonConvertible, IInferableInvokable, IConvertibleMetaObject, IPythonGetable { + public MetaPythonFunction(Expression/*!*/ expression, BindingRestrictions/*!*/ restrictions, PythonFunction/*!*/ value) + : base(expression, BindingRestrictions.Empty, value) { + Assert.NotNull(value); + } + + #region IPythonInvokable Members + + public DynamicMetaObject/*!*/ Invoke(PythonInvokeBinder/*!*/ pythonInvoke, Expression/*!*/ codeContext, DynamicMetaObject/*!*/ target, DynamicMetaObject/*!*/[]/*!*/ args) { + return new FunctionBinderHelper(pythonInvoke, this, codeContext, args).MakeMetaObject(); + } + + #endregion + + + #region IPythonGetable Members + + public DynamicMetaObject GetMember(PythonGetMemberBinder member, DynamicMetaObject codeContext) { + return BindGetMemberWorker(member, member.Name, codeContext); + } + + #endregion + + #region MetaObject Overrides + + public override DynamicMetaObject/*!*/ BindInvokeMember(InvokeMemberBinder/*!*/ action, DynamicMetaObject/*!*/[]/*!*/ args) { + ParameterExpression tmp = Expression.Parameter(typeof(object)); + + // first get the default binder value + DynamicMetaObject fallback = action.FallbackInvokeMember(this, args); + + // then fallback w/ an error suggestion that does a late bound lookup. + return action.FallbackInvokeMember( + this, + args, + new DynamicMetaObject( + Ast.Block( + new[] { tmp }, + Ast.Condition( + Ast.NotEqual( + Ast.Assign( + tmp, + Ast.Call( + typeof(PythonOps).GetMethod("PythonFunctionGetMember"), + AstUtils.Convert( + Expression, + typeof(PythonFunction) + ), + Expression.Constant(action.Name) + ) + ), + Ast.Constant(OperationFailed.Value) + ), + action.FallbackInvoke( + new DynamicMetaObject(tmp, BindingRestrictions.Empty), + args, + null + ).Expression, + AstUtils.Convert(fallback.Expression, typeof(object)) + ) + ), + BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)).Merge(fallback.Restrictions) + ) + ); + } + + public override DynamicMetaObject/*!*/ BindInvoke(InvokeBinder/*!*/ call, params DynamicMetaObject/*!*/[]/*!*/ args) { + return new FunctionBinderHelper(call, this, null, args).MakeMetaObject(); + } + + public override DynamicMetaObject BindConvert(ConvertBinder/*!*/ conversion) { + return ConvertWorker(conversion, conversion.Type, conversion.Explicit ? ConversionResultKind.ExplicitCast : ConversionResultKind.ImplicitCast); + } + + public DynamicMetaObject BindConvert(PythonConversionBinder binder) { + return ConvertWorker(binder, binder.Type, binder.ResultKind); + } + + public DynamicMetaObject ConvertWorker(DynamicMetaObjectBinder binder, Type type, ConversionResultKind kind) { + if (type.IsSubclassOf(typeof(Delegate))) { + return MakeDelegateTarget(binder, type, Restrict(typeof(PythonFunction))); + } + return FallbackConvert(binder); + } + + public override System.Collections.Generic.IEnumerable GetDynamicMemberNames() { + foreach (object o in Value.__dict__.Keys) { + if (o is string) { + yield return (string)o; + } + } + } + + public override DynamicMetaObject BindGetMember(GetMemberBinder binder) { + return BindGetMemberWorker(binder, binder.Name, PythonContext.GetCodeContextMO(binder)); + } + + private DynamicMetaObject BindGetMemberWorker(DynamicMetaObjectBinder binder, string name, DynamicMetaObject codeContext) { + ParameterExpression tmp = Expression.Parameter(typeof(object)); + + // first get the default binder value + DynamicMetaObject fallback = FallbackGetMember(binder, this, codeContext); + + // then fallback w/ an error suggestion that does a late bound lookup. + return FallbackGetMember( + binder, + this, + codeContext, + new DynamicMetaObject( + Ast.Block( + new[] { tmp }, + Ast.Condition( + Ast.NotEqual( + Ast.Assign( + tmp, + Ast.Call( + typeof(PythonOps).GetMethod("PythonFunctionGetMember"), + AstUtils.Convert( + Expression, + typeof(PythonFunction) + ), + Expression.Constant(name) + ) + ), + Ast.Constant(OperationFailed.Value) + ), + tmp, + AstUtils.Convert(fallback.Expression, typeof(object)) + ) + ), + BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)).Merge(fallback.Restrictions) + ) + ); + } + + private static DynamicMetaObject FallbackGetMember(DynamicMetaObjectBinder binder, DynamicMetaObject self, DynamicMetaObject codeContext) { + return FallbackGetMember(binder, self, codeContext, null); + } + + private static DynamicMetaObject FallbackGetMember(DynamicMetaObjectBinder binder, DynamicMetaObject self, DynamicMetaObject codeContext, DynamicMetaObject errorSuggestion) { + PythonGetMemberBinder pyGetMem = binder as PythonGetMemberBinder; + if (pyGetMem != null) { + return pyGetMem.Fallback(self, codeContext, errorSuggestion); + } + + return ((GetMemberBinder)binder).FallbackGetMember(self, errorSuggestion); + } + + public override DynamicMetaObject BindSetMember(SetMemberBinder binder, DynamicMetaObject value) { + ParameterExpression tmp = Expression.Parameter(typeof(bool)); + + // fallback w/ an error suggestion that does a late bound set + return binder.FallbackSetMember( + this, + value, + new DynamicMetaObject( + Ast.Call( + typeof(PythonOps).GetMethod("PythonFunctionSetMember"), + AstUtils.Convert( + Expression, + typeof(PythonFunction) + ), + Expression.Constant(binder.Name), + AstUtils.Convert( + value.Expression, + typeof(object) + ) + ), + BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)) + ) + ); + } + + public override DynamicMetaObject BindDeleteMember(DeleteMemberBinder binder) { + switch (binder.Name) { + case "func_dict": + case "__dict__": + return new DynamicMetaObject( + Expression.Call( + typeof(PythonOps).GetMethod("PythonFunctionDeleteDict") + ), + BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)) + ); + case "__doc__": + case "func_doc": + return new DynamicMetaObject( + Expression.Call( + typeof(PythonOps).GetMethod("PythonFunctionDeleteDoc"), + Expression.Convert(Expression, typeof(PythonFunction)) + ), + BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)) + ); + case "func_defaults": + return new DynamicMetaObject( + Expression.Call( + typeof(PythonOps).GetMethod("PythonFunctionDeleteDefaults"), + Expression.Convert(Expression, typeof(PythonFunction)) + ), + BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)) + ); + } + + // first get the default binder value + DynamicMetaObject fallback = binder.FallbackDeleteMember(this); + + // then fallback w/ an error suggestion that does a late bound delete + return binder.FallbackDeleteMember( + this, + new DynamicMetaObject( + Expression.Condition( + Ast.Call( + typeof(PythonOps).GetMethod("PythonFunctionDeleteMember"), + AstUtils.Convert( + Expression, + typeof(PythonFunction) + ), + Expression.Constant(binder.Name) + ), + Expression.Default(typeof(void)), // we deleted the member + AstUtils.Convert( + fallback.Expression, // report language specific error, + typeof(void) + ) + ), + BindingRestrictions.GetTypeRestriction(Expression, typeof(PythonFunction)).Merge(fallback.Restrictions) + ) + ); + } + + #endregion + + #region Calls + + /// + /// Performs the actual work of binding to the function. + /// + /// Overall this works by going through the arguments and attempting to bind all the outstanding known + /// arguments - position arguments and named arguments which map to parameters are easy and handled + /// in the 1st pass for GetArgumentsForRule. We also pick up any extra named or position arguments which + /// will need to be passed off to a kw argument or a params array. + /// + /// After all the normal args have been assigned to do a 2nd pass in FinishArguments. Here we assign + /// a value to either a value from the params list, kw-dict, or defaults. If there is ambiguity between + /// this (e.g. we have a splatted params list, kw-dict, and defaults) we call a helper which extracts them + /// in the proper order (first try the list, then the dict, then the defaults). + /// + class FunctionBinderHelper { + private readonly MetaPythonFunction/*!*/ _func; // the meta object for the function we're calling + private readonly DynamicMetaObject/*!*/[]/*!*/ _args; // the arguments for the function + private readonly DynamicMetaObject/*!*/[]/*!*/ _originalArgs; // the original arguments for the function + private readonly DynamicMetaObjectBinder/*!*/ _call; // the signature for the method call + private readonly Expression _codeContext; // the code context expression if one is available. + + private List/*!*/ _temps; // temporary variables allocated to create the rule + private ParameterExpression _dict, _params, _paramsLen; // splatted dictionary & params + the initial length of the params array, null if not provided. + private List _init; // a set of initialization code (e.g. creating a list for the params array) + private Expression _error; // a custom error expression if the default needs to be overridden. + private bool _extractedParams; // true if we needed to extract a parameter from the parameter list. + private bool _extractedDefault; // true if we needed to extract a parameter from the kw list. + private bool _needCodeTest; // true if we need to test the code object + private Expression _deferTest; // non-null if we have a test which could fail at runtime and we need to fallback to deferal + private Expression _userProvidedParams; // expression the user provided that should be expanded for params. + private Expression _paramlessCheck; // tests when we have no parameters + + public FunctionBinderHelper(DynamicMetaObjectBinder/*!*/ call, MetaPythonFunction/*!*/ function, Expression codeContext, DynamicMetaObject/*!*/[]/*!*/ args) { + PerfTrack.NoteEvent(PerfTrack.Categories.Binding, "PythonFunction Invoke " + function.Value.FunctionCompatibility + " w/ " + args.Length + " args"); + PerfTrack.NoteEvent(PerfTrack.Categories.BindingTarget, "PythonFunction"); + + _call = call; + _func = function; + _args = args; + _originalArgs = args; + _temps = new List(); + _codeContext = codeContext; + + // Remove the passed in instance argument if present + int instanceIndex = Signature.IndexOf(ArgumentType.Instance); + if (instanceIndex > -1) { + _args = ArrayUtils.RemoveAt(_args, instanceIndex); + } + } + + public DynamicMetaObject/*!*/ MakeMetaObject() { + Expression[] invokeArgs = GetArgumentsForRule(); + BindingRestrictions restrict = _func.Restrictions.Merge(GetRestrictions().Merge(BindingRestrictions.Combine(_args))); + DynamicMetaObject res; + + if (invokeArgs != null) { + // successful call + Expression target = AddInitialization(MakeFunctionInvoke(invokeArgs)); + + if (_temps.Count > 0) { + target = Ast.Block( + _temps, + target + ); + } + + res = new DynamicMetaObject( + target, + restrict + ); + } else if (_error != null) { + // custom error generated while figuring out the call + res = new DynamicMetaObject(_error, restrict); + } else { + // generic error + res = new DynamicMetaObject( + _call.Throw( + Ast.Call( + typeof(PythonOps).GetMethod(Signature.HasKeywordArgument() ? "BadKeywordArgumentError" : "FunctionBadArgumentError"), + AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), + AstUtils.Constant(Signature.GetProvidedPositionalArgumentCount()) + ), + typeof(object) + ), + restrict + ); + } + + DynamicMetaObject[] deferArgs = ArrayUtils.Insert(_func, _originalArgs); + + if (_codeContext != null) { + deferArgs = ArrayUtils.Insert(new DynamicMetaObject(_codeContext, BindingRestrictions.Empty), deferArgs); + } + + return BindingHelpers.AddDynamicTestAndDefer( + _call, + res, + deferArgs, + new ValidationInfo(_deferTest), + res.Expression.Type // force defer to our return type, our restrictions guarantee this to be true (only defaults can change, and we restrict to the delegate type) + ); + } + + + private CallSignature Signature { + get { + return BindingHelpers.GetCallSignature(_call); + } + } + + /// + /// Makes the test for our rule. + /// + private BindingRestrictions/*!*/ GetRestrictions() { + if (!Signature.HasKeywordArgument()) { + return GetSimpleRestriction(); + } + + return GetComplexRestriction(); + } + + /// + /// Makes the test when we just have simple positional arguments. + /// + private BindingRestrictions/*!*/ GetSimpleRestriction() { + _deferTest = Ast.Equal( + Ast.Call( + typeof(PythonOps).GetMethod("FunctionGetCompatibility"), + Ast.Convert(_func.Expression, typeof(PythonFunction)) + ), + AstUtils.Constant(_func.Value.FunctionCompatibility) + ); + + return BindingRestrictionsHelpers.GetRuntimeTypeRestriction( + _func.Expression, typeof(PythonFunction) + ); + } + + /// + /// Makes the test when we have a keyword argument call or splatting. + /// + /// + private BindingRestrictions/*!*/ GetComplexRestriction() { + if (_extractedDefault) { + return BindingRestrictions.GetInstanceRestriction(_func.Expression, _func.Value); + } else if (_needCodeTest) { + return GetSimpleRestriction().Merge( + BindingRestrictions.GetInstanceRestriction( + Expression.Property( + GetFunctionParam(), + "__code__" + ), + _func.Value.__code__ + ) + ); + } + + return GetSimpleRestriction(); + } + + /// + /// Gets the array of expressions which correspond to each argument for the function. These + /// correspond with the function as it's defined in Python and must be transformed for our + /// delegate type before being used. + /// + private Expression/*!*/[]/*!*/ GetArgumentsForRule() { + Expression[] exprArgs = new Expression[_func.Value.NormalArgumentCount + _func.Value.ExtraArguments]; + List extraArgs = null; + Dictionary namedArgs = null; + int instanceIndex = Signature.IndexOf(ArgumentType.Instance); + + // walk all the provided args and find out where they go... + for (int i = 0; i < _args.Length; i++) { + int parameterIndex = (instanceIndex == -1 || i < instanceIndex) ? i : i + 1; + + switch (Signature.GetArgumentKind(i)) { + case ArgumentType.Dictionary: + _args[parameterIndex] = MakeDictionaryCopy(_args[parameterIndex]); + continue; + + case ArgumentType.List: + _userProvidedParams = _args[parameterIndex].Expression; + continue; + + case ArgumentType.Named: + _needCodeTest = true; + bool foundName = false; + for (int j = 0; j < _func.Value.NormalArgumentCount; j++) { + if (_func.Value.ArgNames[j] == Signature.GetArgumentName(i)) { + if (exprArgs[j] != null) { + // kw-argument provided for already provided normal argument. + if (_error == null) { + _error = _call.Throw( + Expression.Call( + typeof(PythonOps).GetMethod("MultipleKeywordArgumentError"), + GetFunctionParam(), + Expression.Constant(_func.Value.ArgNames[j]) + ), + typeof(object) + ); + } + return null; + } + + exprArgs[j] = _args[parameterIndex].Expression; + foundName = true; + break; + } + } + + if (!foundName) { + if (namedArgs == null) { + namedArgs = new Dictionary(); + } + namedArgs[Signature.GetArgumentName(i)] = _args[parameterIndex].Expression; + } + continue; + } + + if (i < _func.Value.NormalArgumentCount) { + exprArgs[i] = _args[parameterIndex].Expression; + } else { + if (extraArgs == null) { + extraArgs = new List(); + } + extraArgs.Add(_args[parameterIndex].Expression); + } + } + + if (!FinishArguments(exprArgs, extraArgs, namedArgs)) { + if (namedArgs != null && _func.Value.ExpandDictPosition == -1) { + MakeUnexpectedKeywordError(namedArgs); + } + + return null; + } + + return GetArgumentsForTargetType(exprArgs); + } + + /// + /// Binds any missing arguments to values from params array, kw dictionary, or default values. + /// + private bool FinishArguments(Expression[] exprArgs, List paramsArgs, Dictionary namedArgs) { + int noDefaults = _func.Value.NormalArgumentCount - _func.Value.Defaults.Length; // number of args w/o defaults + + for (int i = 0; i < _func.Value.NormalArgumentCount; i++) { + if (exprArgs[i] != null) { + if (_userProvidedParams != null && i >= Signature.GetProvidedPositionalArgumentCount()) { + exprArgs[i] = ValidateNotDuplicate(exprArgs[i], _func.Value.ArgNames[i], i); + } + continue; + } + + if (i < noDefaults) { + exprArgs[i] = ExtractNonDefaultValue(_func.Value.ArgNames[i]); + if (exprArgs[i] == null) { + // can't get a value, this is an invalid call. + return false; + } + } else { + exprArgs[i] = ExtractDefaultValue(i, i - noDefaults); + } + } + + if (!TryFinishList(exprArgs, paramsArgs) || + !TryFinishDictionary(exprArgs, namedArgs)) + return false; + + // add check for extra parameters. + AddCheckForNoExtraParameters(exprArgs); + + return true; + } + + /// + /// Creates the argument for the list expansion parameter. + /// + private bool TryFinishList(Expression[] exprArgs, List paramsArgs) { + if (_func.Value.ExpandListPosition != -1) { + if (_userProvidedParams != null) { + if (_params == null && paramsArgs == null) { + // we didn't extract any params, we can re-use a Tuple or + // make a single copy. + exprArgs[_func.Value.ExpandListPosition] = Ast.Call( + typeof(PythonOps).GetMethod("GetOrCopyParamsTuple"), + GetFunctionParam(), + AstUtils.Convert(_userProvidedParams, typeof(object)) + ); + } else { + // user provided a sequence to be expanded, and we may have used it, + // or we have extra args. + EnsureParams(); + + exprArgs[_func.Value.ExpandListPosition] = Ast.Call( + typeof(PythonOps).GetMethod("MakeTupleFromSequence"), + AstUtils.Convert(_params, typeof(object)) + ); + + if (paramsArgs != null) { + MakeParamsAddition(paramsArgs); + } + } + } else { + exprArgs[_func.Value.ExpandListPosition] = MakeParamsTuple(paramsArgs); + } + } else if (paramsArgs != null) { + // extra position args which are unused and no where to put them. + return false; + } + return true; + } + + /// + /// Adds extra positional arguments to the start of the expanded list. + /// + private void MakeParamsAddition(List paramsArgs) { + _extractedParams = true; + + List args = new List(paramsArgs.Count + 1); + args.Add(_params); + args.AddRange(paramsArgs); + + EnsureInit(); + + _init.Add( + AstUtils.ComplexCallHelper( + typeof(PythonOps).GetMethod("AddParamsArguments"), + args.ToArray() + ) + ); + } + + /// + /// Creates the argument for the dictionary expansion parameter. + /// + private bool TryFinishDictionary(Expression[] exprArgs, Dictionary namedArgs) { + if (_func.Value.ExpandDictPosition != -1) { + if (_dict != null) { + // used provided a dictionary to be expanded + exprArgs[_func.Value.ExpandDictPosition] = _dict; + if (namedArgs != null) { + foreach (KeyValuePair kvp in namedArgs) { + MakeDictionaryAddition(kvp); + } + } + } else { + exprArgs[_func.Value.ExpandDictPosition] = MakeDictionary(namedArgs); + } + } else if (namedArgs != null) { + // extra named args which are unused and no where to put them. + return false; + } + return true; + } + + /// + /// Adds an unbound keyword argument into the dictionary. + /// + /// + private void MakeDictionaryAddition(KeyValuePair kvp) { + _init.Add( + Ast.Call( + typeof(PythonOps).GetMethod("AddDictionaryArgument"), + AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), + AstUtils.Constant(kvp.Key), + AstUtils.Convert(kvp.Value, typeof(object)), + AstUtils.Convert(_dict, typeof(PythonDictionary)) + ) + ); + } + + /// + /// Adds a check to the last parameter (so it's evaluated after we've extracted + /// all the parameters) to ensure that we don't have any extra params or kw-params + /// when we don't have a params array or params dict to expand them into. + /// + private void AddCheckForNoExtraParameters(Expression[] exprArgs) { + List tests = new List(3); + + // test we've used all of the extra parameters + if (_func.Value.ExpandListPosition == -1) { + if (_params != null) { + // we used some params, they should have gone down to zero... + tests.Add( + Ast.Call( + typeof(PythonOps).GetMethod("CheckParamsZero"), + AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), + _params + ) + ); + } else if (_userProvidedParams != null) { + // the user provided params, we didn't need any, and they should be zero + tests.Add( + Ast.Call( + typeof(PythonOps).GetMethod("CheckUserParamsZero"), + AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), + AstUtils.Convert(_userProvidedParams, typeof(object)) + ) + ); + } + } + + // test that we've used all the extra named arguments + if (_func.Value.ExpandDictPosition == -1 && _dict != null) { + tests.Add( + Ast.Call( + typeof(PythonOps).GetMethod("CheckDictionaryZero"), + AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), + AstUtils.Convert(_dict, typeof(IDictionary)) + ) + ); + } + + if (tests.Count != 0) { + if (exprArgs.Length != 0) { + // if we have arguments run the tests after the last arg is evaluated. + Expression last = exprArgs[exprArgs.Length - 1]; + + ParameterExpression temp; + + _temps.Add(temp = Ast.Variable(last.Type, "$temp")); + + tests.Insert(0, Ast.Assign(temp, last)); + tests.Add(temp); + exprArgs[exprArgs.Length - 1] = Ast.Block(tests.ToArray()); + } else { + // otherwise run them right before the method call + _paramlessCheck = Ast.Block(tests.ToArray()); + } + } + } + + /// + /// Helper function to validate that a named arg isn't duplicated with by + /// a params list or the dictionary (or both). + /// + private Expression ValidateNotDuplicate(Expression value, string name, int position) { + EnsureParams(); + + return Ast.Block( + Ast.Call( + typeof(PythonOps).GetMethod("VerifyUnduplicatedByPosition"), + AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), // function + AstUtils.Constant(name, typeof(string)), // name + AstUtils.Constant(position), // position + _paramsLen // params list length + ), + value + ); + } + + /// + /// Helper function to get a value (which has no default) from either the + /// params list or the dictionary (or both). + /// + private Expression ExtractNonDefaultValue(string name) { + if (_userProvidedParams != null) { + // expanded params + if (_dict != null) { + // expanded params & dict + return ExtractFromListOrDictionary(name); + } else { + return ExtractNextParamsArg(); + } + } else if (_dict != null) { + // expanded dict + return ExtractDictionaryArgument(name); + } + + // missing argument, no default, no expanded params or dict. + return null; + } + + /// + /// Helper function to get the specified variable from the dictionary. + /// + private Expression ExtractDictionaryArgument(string name) { + _needCodeTest = true; + + return Ast.Call( + typeof(PythonOps).GetMethod("ExtractDictionaryArgument"), + AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), // function + AstUtils.Constant(name, typeof(string)), // name + AstUtils.Constant(Signature.ArgumentCount), // arg count + AstUtils.Convert(_dict, typeof(PythonDictionary)) // dictionary + ); + } + + /// + /// Helper function to extract the variable from defaults, or to call a helper + /// to check params / kw-dict / defaults to see which one contains the actual value. + /// + private Expression ExtractDefaultValue(int index, int dfltIndex) { + if (_dict == null && _userProvidedParams == null) { + // we can pull the default directly + return Ast.Call( + typeof(PythonOps).GetMethod("FunctionGetDefaultValue"), + AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), + AstUtils.Constant(dfltIndex) + ); + } else { + // we might have a conflict, check the default last. + if (_userProvidedParams != null) { + EnsureParams(); + } + _extractedDefault = true; + return Ast.Call( + typeof(PythonOps).GetMethod("GetFunctionParameterValue"), + AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), + AstUtils.Constant(dfltIndex), + AstUtils.Constant(_func.Value.ArgNames[index], typeof(string)), + VariableOrNull(_params, typeof(List)), + VariableOrNull(_dict, typeof(PythonDictionary)) + ); + } + } + + /// + /// Helper function to extract from the params list or dictionary depending upon + /// which one has an available value. + /// + private Expression ExtractFromListOrDictionary(string name) { + EnsureParams(); + + _needCodeTest = true; + + return Ast.Call( + typeof(PythonOps).GetMethod("ExtractAnyArgument"), + AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), // function + AstUtils.Constant(name, typeof(string)), // name + _paramsLen, // arg count + _params, // params list + AstUtils.Convert(_dict, typeof(IDictionary)) // dictionary + ); + } + + private void EnsureParams() { + if (!_extractedParams) { + Debug.Assert(_userProvidedParams != null); + MakeParamsCopy(_userProvidedParams); + _extractedParams = true; + } + } + + /// + /// Helper function to extract the next argument from the params list. + /// + private Expression ExtractNextParamsArg() { + if (!_extractedParams) { + MakeParamsCopy(_userProvidedParams); + + _extractedParams = true; + } + + return Ast.Call( + typeof(PythonOps).GetMethod("ExtractParamsArgument"), + AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), // function + AstUtils.Constant(Signature.ArgumentCount), // arg count + _params // list + ); + } + + private static Expression VariableOrNull(ParameterExpression var, Type type) { + if (var != null) { + return AstUtils.Convert( + var, + type + ); + } + return AstUtils.Constant(null, type); + } + + /// + /// Fixes up the argument list for the appropriate target delegate type. + /// + private Expression/*!*/[]/*!*/ GetArgumentsForTargetType(Expression[] exprArgs) { + Type target = _func.Value.func_code.Target.GetType(); + if (target == typeof(Func)) { + exprArgs = new Expression[] { + AstUtils.NewArrayHelper(typeof(object), exprArgs) + }; + } + + return exprArgs; + } + + /// + /// Helper function to get the function argument strongly typed. + /// + private UnaryExpression/*!*/ GetFunctionParam() { + return Ast.Convert(_func.Expression, typeof(PythonFunction)); + } + + /// + /// Called when the user is expanding a dictionary - we copy the user + /// dictionary and verify that it contains only valid string names. + /// + private DynamicMetaObject/*!*/ MakeDictionaryCopy(DynamicMetaObject/*!*/ userDict) { + Debug.Assert(_dict == null); + + userDict = userDict.Restrict(userDict.GetLimitType()); + _temps.Add(_dict = Ast.Variable(typeof(PythonDictionary), "$dict")); + + EnsureInit(); + + string methodName; + if (typeof(PythonDictionary).IsAssignableFrom(userDict.GetLimitType())) { + methodName = "CopyAndVerifyPythonDictionary"; + } else if (typeof(IDictionary).IsAssignableFrom(userDict.GetLimitType())) { + methodName = "CopyAndVerifyDictionary"; + } else { + methodName = "CopyAndVerifyUserMapping"; + } + + _init.Add( + Ast.Assign( + _dict, + Ast.Call( + typeof(PythonOps).GetMethod(methodName), + GetFunctionParam(), + AstUtils.Convert(userDict.Expression, userDict.GetLimitType()) + ) + ) + ); + return userDict; + } + + /// + /// Called when the user is expanding a params argument + /// + private void MakeParamsCopy(Expression/*!*/ userList) { + Debug.Assert(_params == null); + + _temps.Add(_params = Ast.Variable(typeof(List), "$list")); + _temps.Add(_paramsLen = Ast.Variable(typeof(int), "$paramsLen")); + + EnsureInit(); + + _init.Add( + Ast.Assign( + _params, + Ast.Call( + typeof(PythonOps).GetMethod("CopyAndVerifyParamsList"), + AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), + AstUtils.Convert(userList, typeof(object)) + ) + ) + ); + + _init.Add( + Ast.Assign(_paramsLen, + Ast.Add( + Ast.Call(_params, typeof(List).GetMethod("__len__")), + AstUtils.Constant(Signature.GetProvidedPositionalArgumentCount()) + ) + ) + ); + } + + /// + /// Called when the user hasn't supplied a dictionary to be expanded but the + /// function takes a dictionary to be expanded. + /// + private Expression MakeDictionary(Dictionary namedArgs) { + Debug.Assert(_dict == null); + _temps.Add(_dict = Ast.Variable(typeof(PythonDictionary), "$dict")); + + Expression dictCreator; + ParameterExpression dictRef = _dict; + + if (namedArgs != null) { + Debug.Assert(namedArgs.Count > 0); + + Expression[] items = new Expression[namedArgs.Count * 2]; + int itemIndex = 0; + foreach (KeyValuePair kvp in namedArgs) { + items[itemIndex++] = AstUtils.Convert(kvp.Value, typeof(object)); + items[itemIndex++] = AstUtils.Constant(kvp.Key, typeof(object)); + } + + dictCreator = Ast.Assign( + _dict, + Ast.Call( + typeof(PythonOps).GetMethod("MakeHomogeneousDictFromItems"), + Ast.NewArrayInit(typeof(object), items) + ) + ); + + } else { + dictCreator = Ast.Assign( + _dict, + Ast.Call( + typeof(PythonOps).GetMethod("MakeDict"), + AstUtils.Constant(0) + ) + ); + } + + return dictCreator; + } + + /// + /// Helper function to create the expression for creating the actual tuple passed through. + /// + private static Expression/*!*/ MakeParamsTuple(List extraArgs) { + if (extraArgs != null) { + return AstUtils.ComplexCallHelper( + typeof(PythonOps).GetMethod("MakeTuple"), + extraArgs.ToArray() + ); + } + return Ast.Call( + typeof(PythonOps).GetMethod("MakeTuple"), + Ast.NewArrayInit(typeof(object[])) + ); + } + + /// + /// Creates the code to invoke the target delegate function w/ the specified arguments. + /// + private Expression/*!*/ MakeFunctionInvoke(Expression[] invokeArgs) { + Type targetType = _func.Value.func_code.Target.GetType(); + MethodInfo method = targetType.GetMethod("Invoke"); + + // If calling generator, create the instance of PythonGenerator first + // and add it into the list of arguments + invokeArgs = ArrayUtils.Insert(GetFunctionParam(), invokeArgs); + + Expression invoke = AstUtils.SimpleCallHelper( + Ast.Convert( + Ast.Call( + _call.SupportsLightThrow() ? + typeof(PythonOps).GetMethod("FunctionGetLightThrowTarget") + : typeof(PythonOps).GetMethod("FunctionGetTarget"), + GetFunctionParam() + ), + targetType + ), + method, + invokeArgs + ); + + if (_paramlessCheck != null) { + invoke = Expression.Block(_paramlessCheck, invoke); + } + + return invoke; + } + + /// + /// Appends the initialization code for the call to the function if any exists. + /// + private Expression/*!*/ AddInitialization(Expression body) { + if (_init == null) return body; + + List res = new List(_init); + res.Add(body); + return Ast.Block(res); + } + + private void MakeUnexpectedKeywordError(Dictionary namedArgs) { + string name = null; + foreach (string id in namedArgs.Keys) { + name = id; + break; + } + + _error = _call.Throw( + Ast.Call( + typeof(PythonOps).GetMethod("UnexpectedKeywordArgumentError"), + AstUtils.Convert(GetFunctionParam(), typeof(PythonFunction)), + AstUtils.Constant(name, typeof(string)) + ), + typeof(PythonOps) + ); + } + + private void EnsureInit() { + if (_init == null) _init = new List(); + } + } + + #endregion + + #region Operations + + private static DynamicMetaObject/*!*/ MakeCallSignatureRule(DynamicMetaObject self) { + return new DynamicMetaObject( + Ast.Call( + typeof(PythonOps).GetMethod("GetFunctionSignature"), + AstUtils.Convert( + self.Expression, + typeof(PythonFunction) + ) + ), + BindingRestrictionsHelpers.GetRuntimeTypeRestriction(self.Expression, typeof(PythonFunction)) + ); + } + + private static DynamicMetaObject MakeIsCallableRule(DynamicMetaObject/*!*/ self) { + return new DynamicMetaObject( + AstUtils.Constant(true), + BindingRestrictionsHelpers.GetRuntimeTypeRestriction(self.Expression, typeof(PythonFunction)) + ); + } + + #endregion + + #region Helpers + + public new PythonFunction/*!*/ Value { + get { + return (PythonFunction)base.Value; + } + } + + #endregion + + #region IPythonOperable Members + + DynamicMetaObject IPythonOperable.BindOperation(PythonOperationBinder action, DynamicMetaObject[] args) { + switch (action.Operation) { + case PythonOperationKind.CallSignatures: + return MakeCallSignatureRule(this); + case PythonOperationKind.IsCallable: + return MakeIsCallableRule(this); + } + + return null; + } + + #endregion + + #region IInvokableInferable Members + + InferenceResult IInferableInvokable.GetInferredType(Type delegateType, Type parameterType) { + if (!delegateType.IsSubclassOf(typeof(Delegate))) { + throw new InvalidOperationException(); + } + + MethodInfo invoke = delegateType.GetMethod("Invoke"); + ParameterInfo[] pis = invoke.GetParameters(); + if (pis.Length == Value.NormalArgumentCount) { + // our signatures are compatible + return new InferenceResult( + typeof(object), + Restrictions.Merge( + BindingRestrictions.GetTypeRestriction( + Expression, + typeof(PythonFunction) + ).Merge( + BindingRestrictions.GetExpressionRestriction( + Expression.Equal( + Expression.Call( + typeof(PythonOps).GetMethod("FunctionGetCompatibility"), + Expression.Convert(Expression, typeof(PythonFunction)) + ), + Expression.Constant(Value.FunctionCompatibility) + ) + ) + ) + ) + ); + + } + + return null; + } + + #endregion + + #region IConvertibleMetaObject Members + + bool IConvertibleMetaObject.CanConvertTo(Type/*!*/ type, bool @explicit) { + return type.IsSubclassOf(typeof(Delegate)); + } + + #endregion + } +} diff --git a/Languages/Ruby/Public/README.txt b/Languages/Ruby/Public/README.txt index 00fe60e468..77f6dea672 100644 --- a/Languages/Ruby/Public/README.txt +++ b/Languages/Ruby/Public/README.txt @@ -1,46 +1,49 @@ -IronRuby - A .NET Implementation of the Ruby language - -Authors: - Daniele Alessandri, Shri Borde, Peter Bacon Darwin, Jim Deville, - Curt Hagenlocher, John Lam, Haibo Luo, Tomas Matousek, John Messerly, - Jirapong Nanta, Srivatsn Narayanan, Jimmy Schementi, Oleg Tkachenko, - Dino Viehland, and everyone else from the community who reports bugs, - builds libraries, and helps enrich IronRuby. - -Project Contact: Jimmy Schementi - -== About - -IronRuby is a Open Source implementation of the Ruby programming language -(http://www.ruby-lang.org) for .NET, heavily relying on Microsoft's -Dynamic Language Runtime (http://dlr.codeplex.com). - -The project's #1 goal is to be a true Ruby implementation, meaning it runs -existing Ruby code. See -http://ironruby.net/Documentation/Real_Ruby_Applications for information about -using the Ruby standard library and 3rd party libraries in IronRuby. - -IronRuby has tightly integration with .NET, so any .NET types can be used from -IronRuby, and the IronRuby runtime can be embedded into any .NET application. -See http://ironruby.net/documentation/.net for more information. - -== Running - -bin/ir.exe rubyfile.rb - -Will run rubyfile.rb with the IronRuby compiler. - -== Package - - /bin IronRuby binaries, ir.exe, iirb, irake, igem, iri, irdoc, etc. - /lib Ruby standard library, including RubyGems - CHANGELOG.txt Changes for each release - RELEASE.txt Release notes - LICENSE.Ruby.txt Ruby license - LICENSE.CPL.txt Common Public License - LICENSE.APACHE.html Apache License, Version 2.0 - README.txt This file - -== License - -Read the License.* files +IronRuby - A .NET Implementation of the Ruby language + +Authors: + Daniele Alessandri, Shri Borde, Peter Bacon Darwin, Jim Deville, + Curt Hagenlocher, John Lam, Haibo Luo, Tomas Matousek, John Messerly, + Jirapong Nanta, Srivatsn Narayanan, Jimmy Schementi, Oleg Tkachenko, + Dino Viehland, and everyone else from the community who reports bugs, + builds libraries, and helps enrich IronRuby. + +Project Contacts: + Jimmy Schementi + Miguel de Icaza + Tomas Matousek + +== About + +IronRuby is a Open Source implementation of the Ruby programming language +(http://www.ruby-lang.org) for .NET, heavily relying on the +Dynamic Language Runtime (http://dlr.codeplex.com). + +The project's #1 goal is to be a true Ruby implementation, meaning it runs +existing Ruby code. See +http://ironruby.net/Documentation/Real_Ruby_Applications for information about +using the Ruby standard library and 3rd party libraries in IronRuby. + +IronRuby has tightly integration with .NET, so any .NET types can be used from +IronRuby, and the IronRuby runtime can be embedded into any .NET application. +See http://ironruby.net/documentation/.net for more information. + +== Running + +bin/ir.exe rubyfile.rb + +Will run rubyfile.rb with the IronRuby compiler. + +== Package + + /bin IronRuby binaries, ir.exe, iirb, irake, igem, iri, irdoc, etc. + /lib Ruby standard library, including RubyGems + CHANGELOG.txt Changes for each release + RELEASE.txt Release notes + LICENSE.Ruby.txt Ruby license + LICENSE.CPL.txt Common Public License + LICENSE.APACHE.html Apache License, Version 2.0 + README.txt This file + +== License + +Read the License.* files diff --git a/Msi/IronStudio/IronStudio.msm.wproj b/Msi/IronStudio/IronStudio.msm.wproj index 7bb0be6e4f..e9453b2b8e 100644 --- a/Msi/IronStudio/IronStudio.msm.wproj +++ b/Msi/IronStudio/IronStudio.msm.wproj @@ -1,49 +1,49 @@ - - - - - Debug - AnyCPU - {5B4E8D5A-1066-4FFE-894A-448CDD868F0B} - SAK - SAK - SAK - SAK - $(SolutionDir)..\bin\$(Configuration)\ - $(SolutionDir)..\Tools\IronStudio\IronStudio\obj\$(Configuration)\ - - - - $(SolutionDir)..\Util\Wix35\ - $(SolutionDir)..\Util\Wix35\ - $(WixExtDir)\WixTasks.dll - IronStudio - Module - - PrivateBinDir=$(PrivateBinDir); - OutputPath=$(OutputPath); - $(DefineConstants) - - - - - {C98DE16D-417E-4079-8939-1F8C8E28E2C8} - IronStudio - - - {843716AE-38B3-4723-963C-950DD06BC4B8} - IronStudioCore - - - {D10C905C-7F15-41DF-9FF9-CCE461F571FD} - RemoteScriptFactory - - - - - - - - - + + + + + Debug + AnyCPU + {5B4E8D5A-1066-4FFE-894A-448CDD868F0B} + SAK + SAK + SAK + SAK + $(SolutionDir)..\bin\$(Configuration)\ + $(SolutionDir)..\Tools\IronStudio\IronStudio\obj\$(Configuration)\ + + + + $(SolutionDir)..\Util\Wix35\ + $(SolutionDir)..\Util\Wix35\ + $(WixExtDir)\WixTasks.dll + IronStudio + Module + + PrivateBinDir=$(PrivateBinDir); + OutputPath=$(OutputPath); + $(DefineConstants) + + + + + {C98DE16D-417E-4079-8939-1F8C8E28E2C8} + IronStudio + + + {843716AE-38B3-4723-963C-950DD06BC4B8} + IronStudioCore + + + {D10C905C-7F15-41DF-9FF9-CCE461F571FD} + RemoteScriptFactory + + + + + + + + + \ No newline at end of file diff --git a/Msi/Python/Msi/IronPython.Msi.wproj b/Msi/Python/Msi/IronPython.Msi.wproj index babe1afc71..2512550374 100644 --- a/Msi/Python/Msi/IronPython.Msi.wproj +++ b/Msi/Python/Msi/IronPython.Msi.wproj @@ -1,86 +1,86 @@ - - - - - Debug - AnyCPU - {01E7EFAA-3BCC-4024-BBCE-015B5D12FFC8} - SAK - SAK - SAK - SAK - false - IronPython - Package - - - $(SolutionDir)\..\Languages\IronPython - $(SolutionDir)..\Tools\IronStudio\IronPythonTools - $(VsToolsDir)\Templates - $(SolutionDir)..\Util\Wix35\ - $(SolutionDir)..\Util\Wix35\ - $(WixExtDir)\WixTasks.dll - $(SolutionDir)..\Bin\$(Configuration) - - - $(SolutionDir)..\Bin\$(Configuration) - - - PythonDir=$(PythonDir); - VsToolsDir=$(VsToolsDir); - VsTemplatesDir=$(VsTemplatesDir); - VsToolsPrivateBinDir=$(VsToolsDir)\obj\$(Configuration); - SilverlightScriptDir=$(SolutionDir)..\Hosts\SilverLight\Public\script; - SilverlightDir=$(SolutionDir)..\Bin\Silverlight4$(Configuration); - TutorialDir=$(PythonDir)\Public\Tutorial; - ToolsDir=$(PythonDir)\Public\Tools; - DocDir=$(PythonDir)\Public\Doc; - $(DefineConstants) - - - - - - - - - {FA7BE5F5-E04F-4613-B7AC-70CE10D1BB68} - IronPythonTools - - - - - {01E7EFAA-3BCC-4024-BBCE-015B5D12FFC8} - IronPython.Msi - - - DLR.Msm - - - IronStudio.Msm - - - - - IpyToolsInstallDir - ToolsTemplateGroup - var.VsTemplatesDir - true - - - INSTALLDIR - ToolsGroup - var.ToolsDir - true - - - INSTALLDIR - DocGroup - var.DocDir - true - - - - - + + + + + Debug + AnyCPU + {01E7EFAA-3BCC-4024-BBCE-015B5D12FFC8} + SAK + SAK + SAK + SAK + false + IronPython + Package + + + $(SolutionDir)\..\Languages\IronPython + $(SolutionDir)..\Tools\IronStudio\IronPythonTools + $(VsToolsDir)\Templates + $(SolutionDir)..\Util\Wix35\ + $(SolutionDir)..\Util\Wix35\ + $(WixExtDir)\WixTasks.dll + $(SolutionDir)..\Bin\$(Configuration) + + + $(SolutionDir)..\Bin\$(Configuration) + + + PythonDir=$(PythonDir); + VsToolsDir=$(VsToolsDir); + VsTemplatesDir=$(VsTemplatesDir); + VsToolsPrivateBinDir=$(VsToolsDir)\obj\$(Configuration); + SilverlightScriptDir=$(SolutionDir)..\Hosts\SilverLight\Public\script; + SilverlightDir=$(SolutionDir)..\Bin\Silverlight4$(Configuration); + TutorialDir=$(PythonDir)\Public\Tutorial; + ToolsDir=$(PythonDir)\Public\Tools; + DocDir=$(PythonDir)\Public\Doc; + $(DefineConstants) + + + + + + + + + {FA7BE5F5-E04F-4613-B7AC-70CE10D1BB68} + IronPythonTools + + + + + {01E7EFAA-3BCC-4024-BBCE-015B5D12FFC8} + IronPython.Msi + + + DLR.Msm + + + IronStudio.Msm + + + + + IpyToolsInstallDir + ToolsTemplateGroup + var.VsTemplatesDir + true + + + INSTALLDIR + ToolsGroup + var.ToolsDir + true + + + INSTALLDIR + DocGroup + var.DocDir + true + + + + + \ No newline at end of file diff --git a/Msi/Python/Msm/IronPython.Msm.wproj b/Msi/Python/Msm/IronPython.Msm.wproj index f21089439a..7003892018 100644 --- a/Msi/Python/Msm/IronPython.Msm.wproj +++ b/Msi/Python/Msm/IronPython.Msm.wproj @@ -1,69 +1,69 @@ - - - - - Debug - AnyCPU - {A16F2EEB-4434-4680-9ADD-71BC3B0EB575} - SAK - SAK - SAK - SAK - IpyRedist - Module - - - $(SolutionDir)\..\Languages\IronPython - $(SolutionDir)..\bin\$(Configuration)\ - $(SolutionDir)..\Util\Wix35\ - $(SolutionDir)..\Util\Wix35\ - $(WixExtDir)\WixTasks.dll - - - $(SolutionDir)..\Bin\$(Configuration) - - - PythonDir=$(PythonDir); - StdLibPath=$(SolutionDir)..\External.LCA_RESTRICTED/Languages/IronPython/27/Lib; - $(DefineConstants) - - - - - {155CE436-1669-4A48-8095-410F2430237F} - IronPython.Modules - - - {65E997B7-E99B-4C83-B29E-9951429BB293} - IronPython.Wpf - - - {F1D861C5-D9D5-4CDA-968B-8275F5D9F6D2} - IronPythonConsoleAny - - - {811AC32C-11F3-4ED8-92A7-A7E39C2BB704} - IronPythonConsole - - - {7F6F9AB3-9943-4316-BD4C-A10F580BC75C} - IronPythonWindowAny - - - {81DA19C7-4FEC-47E7-981B-D9310D549F95} - IronPythonWindow - - - {95289EA9-5778-489D-AB48-F81F2CE2DA32} - IronPython - - - - - - - - - - + + + + + Debug + AnyCPU + {A16F2EEB-4434-4680-9ADD-71BC3B0EB575} + SAK + SAK + SAK + SAK + IpyRedist + Module + + + $(SolutionDir)\..\Languages\IronPython + $(SolutionDir)..\bin\$(Configuration)\ + $(SolutionDir)..\Util\Wix35\ + $(SolutionDir)..\Util\Wix35\ + $(WixExtDir)\WixTasks.dll + + + $(SolutionDir)..\Bin\$(Configuration) + + + PythonDir=$(PythonDir); + StdLibPath=$(SolutionDir)..\External.LCA_RESTRICTED/Languages/IronPython/27/Lib; + $(DefineConstants) + + + + + {155CE436-1669-4A48-8095-410F2430237F} + IronPython.Modules + + + {65E997B7-E99B-4C83-B29E-9951429BB293} + IronPython.Wpf + + + {F1D861C5-D9D5-4CDA-968B-8275F5D9F6D2} + IronPythonConsoleAny + + + {811AC32C-11F3-4ED8-92A7-A7E39C2BB704} + IronPythonConsole + + + {7F6F9AB3-9943-4316-BD4C-A10F580BC75C} + IronPythonWindowAny + + + {81DA19C7-4FEC-47E7-981B-D9310D549F95} + IronPythonWindow + + + {95289EA9-5778-489D-AB48-F81F2CE2DA32} + IronPython + + + + + + + + + + \ No newline at end of file diff --git a/Msi/Ruby/Msi/IronRuby.Msi.wproj b/Msi/Ruby/Msi/IronRuby.Msi.wproj index ca190c80c0..8607a0a4b9 100644 --- a/Msi/Ruby/Msi/IronRuby.Msi.wproj +++ b/Msi/Ruby/Msi/IronRuby.Msi.wproj @@ -1,50 +1,70 @@ - - - - - Debug - AnyCPU - {9A3E54E8-8B72-4EA3-9275-F98B88D3F6F0} - SAK - SAK - SAK - SAK - false - $(SolutionDir)\..\Languages\Ruby - - - $(SolutionDir)..\bin\Release\ - - - $(SolutionDir)..\bin\Debug\ - - - - $(SolutionDir)..\Util\Wix35\ - $(SolutionDir)..\Util\Wix35\ - $(WixExtDir)\WixTasks.dll - IronRuby - Package - - $(SolutionDir)..\Bin\$(Configuration) - TemplatesDir=$(OutputPath)\Templates;RubyDir=$(RubyDir);SilverlightScriptDir=$(SolutionDir)..\Hosts\SilverLight\Public\script;SilverlightDir=$(SolutionDir)..\Bin\Silverlight4$(Configuration);$(DefineConstants) - - - - - - - - - {A1097A06-AD64-4CC2-A196-A80F918FAD22} - IronRuby.Msi - - - DLR.Msm - - - - - - + + + + + Debug + AnyCPU + {9A3E54E8-8B72-4EA3-9275-F98B88D3F6F0} + SAK + SAK + SAK + SAK + false + IronRuby + Package + + + $(SolutionDir)..\ + $(DlrRoot)Tools\IronStudio\IronRubyTools + $(VsToolsDir)\Templates\ + $(DlrRoot)bin\$(Configuration)\ + $(DlrRoot)Languages\Ruby + $(DlrRoot)Util\Wix35\ + $(DlrRoot)Util\Wix35\ + $(WixExtDir)\WixTasks.dll + + + DlrRoot=$(DlrRoot); + RubyDir=$(RubyDir); + VsToolsDir=$(VsToolsDir); + VsTemplatesDir=$(VsTemplatesDir); + VsToolsPrivateBinDir=$(VsToolsDir)\obj\$(Configuration)\; + SilverlightScriptDir=$(DlrRoot)Hosts\SilverLight\Public\script; + SilverlightDir=$(DlrRoot)Bin\Silverlight4$(Configuration); + $(DefineConstants) + + + + + + + + + + {3467BF9A-3A0F-42BE-85E3-E818E7D3E2E8} + IronPythonTools + + + + + {A1097A06-AD64-4CC2-A196-A80F918FAD22} + IronRuby.Msi + + + DLR.Msm + + + IronStudio.Msm + + + + + IrbToolsInstallDir + ToolsTemplateGroup + var.VsTemplatesDir + true + + + + \ No newline at end of file diff --git a/Msi/Ruby/Msi/IronRuby.wxs b/Msi/Ruby/Msi/IronRuby.wxs index e262bc87bf..23ef37d8f1 100644 --- a/Msi/Ruby/Msi/IronRuby.wxs +++ b/Msi/Ruby/Msi/IronRuby.wxs @@ -1,134 +1,224 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - Privileged - NOT NEWERVERSIONDETECTED - Installed OR MsiNetAssemblySupport >= "4.0.0.0" - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Privileged + NOT NEWERVERSIONDETECTED + Installed OR MsiNetAssemblySupport >= "4.0.0.0" + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + NOT VSINSTALLPATH + + + + + + + + + + + + NOT RUBYFILETYPENODEFAULT + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Msi/Ruby/Msm/IronRuby.Msm.wproj b/Msi/Ruby/Msm/IronRuby.Msm.wproj index 55cb22ef4f..c3264d632d 100644 --- a/Msi/Ruby/Msm/IronRuby.Msm.wproj +++ b/Msi/Ruby/Msm/IronRuby.Msm.wproj @@ -1,63 +1,64 @@ - - - - - Debug - AnyCPU - {A1097A06-AD64-4CC2-A196-A80F918FAD22} - SAK - SAK - SAK - SAK - false - $(SolutionDir)\..\Languages\Ruby - - - $(SolutionDir)..\bin\Release\ - - - $(SolutionDir)..\bin\Debug\ - - - - $(SolutionDir)..\Util\Wix35\ - $(SolutionDir)..\Util\Wix35\ - $(WixExtDir)\WixTasks.dll - IrbRedist - Module - - $(SolutionDir)..\Bin\$(Configuration) - RubyDir=$(RubyDir);DlrRoot=$(SolutionDir)..\;$(DefineConstants) - - - - {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD} - Ruby - - - {77323B06-15A2-4CF4-8A7A-86EAA2B66498} - IronRuby.Libraries - - - {6EE7A428-D803-41BC-8248-1297C3ACE369} - IronRubyConsoleAny - - - {D6AB587D-A888-4B98-85AC-F15E36F53838} - IronRubyConsole - - - {AA18A245-E342-4368-A474-83178311A742} - IronRubyConsole - - - - - - - - - - - - \ No newline at end of file + + + + + Debug + AnyCPU + {A1097A06-AD64-4CC2-A196-A80F918FAD22} + SAK + SAK + SAK + SAK + false + IrbRedist + Module + + + $(SolutionDir)\..\ + $(DlrRoot)Languages\Ruby + $(DlrRoot)bin\$(Configuration)\ + $(DlrRoot)Util\Wix35\ + $(DlrRoot)Util\Wix35\ + $(WixExtDir)\WixTasks.dll + + + $(SolutionDir)..\Bin\$(Configuration) + + + RubyDir=$(RubyDir); + DlrRoot=$(DlrRoot); + $(DefineConstants) + + + + + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD} + Ruby + + + {77323B06-15A2-4CF4-8A7A-86EAA2B66498} + IronRuby.Libraries + + + {6EE7A428-D803-41BC-8248-1297C3ACE369} + IronRubyConsoleAny + + + {D6AB587D-A888-4B98-85AC-F15E36F53838} + IronRubyConsole + + + {AA18A245-E342-4368-A474-83178311A742} + IronRubyConsole + + + + + + + + + + + + diff --git a/Msi/Ruby/Msm/RubyLibs.wxi b/Msi/Ruby/Msm/RubyLibs.wxi index 80655ee95b..585cb44e4a 100644 --- a/Msi/Ruby/Msm/RubyLibs.wxi +++ b/Msi/Ruby/Msm/RubyLibs.wxi @@ -1,888 +1,904 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Msi/Runtime/DLR.Msm.wproj b/Msi/Runtime/DLR.Msm.wproj index 1e91b06cb3..616feca6d3 100644 --- a/Msi/Runtime/DLR.Msm.wproj +++ b/Msi/Runtime/DLR.Msm.wproj @@ -1,50 +1,50 @@ - - - - - Debug - AnyCPU - {81A2FD44-53F8-47E3-9B3B-C0883D140867} - SAK - SAK - SAK - SAK - - - $(SolutionDir)..\bin\Release\ - - - $(SolutionDir)..\bin\Debug\ - - - - $(SolutionDir)..\Util\Wix35\ - $(SolutionDir)..\Util\Wix35\ - $(WixExtDir)\WixTasks.dll - DLR - Module - - $(SolutionDir)..\Bin\$(Configuration) - - - - {02FF0909-F5AD-48CF-A86A-345E721B7E40} - Microsoft.Scripting - - - {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} - Microsoft.Dynamic - - - {ACDD9B9E-8FE6-439C-9521-1CCBA47F6143} - Microsoft.Dynamic - - - - - - - - - + + + + + Debug + AnyCPU + {81A2FD44-53F8-47E3-9B3B-C0883D140867} + SAK + SAK + SAK + SAK + + + $(SolutionDir)..\bin\Release\ + + + $(SolutionDir)..\bin\Debug\ + + + + $(SolutionDir)..\Util\Wix35\ + $(SolutionDir)..\Util\Wix35\ + $(WixExtDir)\WixTasks.dll + DLR + Module + + $(SolutionDir)..\Bin\$(Configuration) + + + + {02FF0909-F5AD-48CF-A86A-345E721B7E40} + Microsoft.Scripting + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Dynamic + + + {ACDD9B9E-8FE6-439C-9521-1CCBA47F6143} + Microsoft.Dynamic + + + + + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/AnalysisTest/AnalysisTest.Perf.cs b/Tools/IronStudio/AnalysisTest/AnalysisTest.Perf.cs new file mode 100644 index 0000000000..e415d39503 --- /dev/null +++ b/Tools/IronStudio/AnalysisTest/AnalysisTest.Perf.cs @@ -0,0 +1,318 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Text; +using IronPython.Hosting; +using IronPython.Runtime; +using IronPython.Runtime.Types; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Hosting.Providers; +using Microsoft.PyAnalysis; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting.Runtime; +using IronPython.Compiler; +using IronPython; +using IronPython.Compiler.Ast; + +namespace AnalysisTest { + public partial class AnalysisTest { + [PerfMethod] + public void TestLookupPerf_Namespaces() { + var entry = ProcessText(@" +import System + "); + + Stopwatch sw = new Stopwatch(); + sw.Start(); + for (int i = 0; i < 1000; i++) { + foreach (var varRef in entry.GetMembers("System", 1)) { + foreach (var innerRef in entry.GetMembers("System." + varRef.Name, 1)) { + } + } + } + sw.Stop(); + Console.WriteLine("{0} ms", sw.ElapsedMilliseconds); + } + + [PerfMethod] + public void TestParsePerf_Decimal() { + string merlin = Environment.GetEnvironmentVariable("DLR_ROOT") ?? @"C:\Product\0\dlr"; + var text = File.ReadAllText(Path.Combine(merlin + @"\External.LCA_RESTRICTED\Languages\IronPython\27\Lib\decimal.py")); + + var sourceUnit = GetSourceUnit(text); + var projectState = new ProjectState(_engine); + Stopwatch sw = new Stopwatch(); + var entry = ParseText(projectState, sourceUnit, "decimal"); + + sw.Start(); + for (int i = 0; i < 5; i++) { + Prepare(entry, sourceUnit); + entry.Analyze(); + } + + sw.Stop(); + Console.WriteLine("{0}", sw.ElapsedMilliseconds); + } + + private static ProjectEntry ParseText(ProjectState state, SourceUnit sourceUnit, string moduleName) { + var newEntry = state.AddModule(moduleName, moduleName, null); + + Prepare(newEntry, sourceUnit); + + foreach (var entry in state.ProjectEntries) { + entry.Analyze(); + } + + return newEntry; + } + + + [PerfMethod] + public void TestLookupPerf_Modules_Class() { + var entry = ProcessText(@" +import System + "); + + Stopwatch sw = new Stopwatch(); + sw.Start(); + + foreach (var result in entry.ProjectState.GetModuleMembers(new[] { "System", "IO", "BinaryReader" })) { + } + foreach (var result in entry.ProjectState.GetModuleMembers(new[] { "System", "IO", "BinaryWriter" })) { + } + foreach (var result in entry.ProjectState.GetModuleMembers(new[] { "System", "IO", "BufferedStream" })) { + } + foreach (var result in entry.ProjectState.GetModuleMembers(new[] { "System", "IO", "Stream" })) { + } + foreach (var result in entry.ProjectState.GetModuleMembers(new[] { "System", "IO", "Directory" })) { + } + foreach (var result in entry.ProjectState.GetModuleMembers(new[] { "System", "IO", "File" })) { + } + foreach (var result in entry.ProjectState.GetModuleMembers(new[] { "System", "IO", "FileStream" })) { + } + + sw.Stop(); + Console.WriteLine(sw.ElapsedMilliseconds); + } + + [PerfMethod] + public void TestLookupPerf_Namespaces2() { + var entry = ProcessText(@" +import System + "); + + Stopwatch sw = new Stopwatch(); + sw.Start(); + foreach (var varRef in entry.GetMembers("System.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.Collections.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.Collections.Generic.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.CodeDom.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.Configuration.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.ComponentModel.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.Deployment.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.Diagnostics.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.Dynamic.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.Globalization.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.Linq.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.Management.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.Media.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.Net.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.Runtime.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.Security.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.Text.", 1)) { + } + foreach (var varRef in entry.GetMembers("System.Threading.", 1)) { + } + + sw.Stop(); + Console.WriteLine("{0} ms", sw.ElapsedMilliseconds); + } + + /// + /// Gets all members from a large number of types + /// + [PerfMethod] + public void TestLookupPerf_Types() { + var entry = ProcessText(@" +import System + "); + + Stopwatch sw = new Stopwatch(); + sw.Start(); + foreach (var varRef in entry.GetMembers("System.", 1)) { + foreach (var innerRef in entry.GetMembers("System." + varRef.Name + ".", 1)) { + } + } + sw.Stop(); + Console.WriteLine("{0} ms", sw.ElapsedMilliseconds); + } + + [PerfMethod] + public void TestLookupPerf_BuiltinModules() { + var builtin_module_names = new[] { "sys", "__builtin__", "exceptions", "clr", "future_builtins", "imp", "array", "binascii", "_sha512", "cmath", "_winreg", "_weakref", "_warnings", "_sre", "_random", "_functools", "xxsubtype", "time", "thread", "_struct", "_heapq", "_ctypes_test", "_ctypes", "socket", "_sha256", "_sha", "select", "re", "operator", "nt", "_md5", "_fileio", "math", "marshal", "_locale", "itertools", "gc", "errno", "datetime", "cStringIO", "cPickle", "copy_reg", "_collections", "_bytesio", "_codecs" }; + StringBuilder text = new StringBuilder(); + foreach (var name in builtin_module_names) { + text.AppendLine("import " + name); + } + var entry = ProcessText(text.ToString()); + + Stopwatch sw = new Stopwatch(); + sw.Start(); + for (int i = 0; i < 50; i++) { + foreach (var name in builtin_module_names) { + foreach (var varRef in entry.GetMembers(name, 1)) { + foreach (var innerRef in entry.GetMembers(name + "." + varRef.Name, 1)) { + } + } + } + } + sw.Stop(); + Console.WriteLine("{0} ms", sw.ElapsedMilliseconds); + } + + [PerfMethod] + public void TestAnalyzeStdLib() { + //string dir = Path.Combine(Environment.GetEnvironmentVariable("ProgramFiles"), "IronPython 2.6 for .NET 4.0 RC\\Lib"); + string dir = Path.Combine("C:\\Python27\\Lib"); + List files = new List(); + CollectFiles(dir, files); + + List sourceUnits = new List(); + foreach (string file in files) { + sourceUnits.Add( + new SourceUnit( + HostingHelpers.GetLanguageContext(_engine), + new FileTextContentProvider(new FileStreamContentProvider(file)), + Path.GetFileNameWithoutExtension(file), + SourceCodeKind.File + ) + ); + } + + Stopwatch sw = new Stopwatch(); + + sw.Start(); + long start0 = sw.ElapsedMilliseconds; + var projectState = new ProjectState(_engine); + List modules = new List(); + PythonOptions EmptyOptions = new PythonOptions(); + foreach (var sourceUnit in sourceUnits) { + modules.Add(projectState.AddModule(Path.GetFileNameWithoutExtension(sourceUnit.Path), sourceUnit.Path, null)); + } + long start1 = sw.ElapsedMilliseconds; + Console.WriteLine("AddSourceUnit: {0} ms", start1 - start0); + + List nodes = new List(); + for (int i = 0; i < modules.Count; i++) { + PythonAst ast = null; + try { + var sourceUnit = sourceUnits[i]; + + var context = new CompilerContext(sourceUnit, HostingHelpers.GetLanguageContext(_engine).GetCompilerOptions(), ErrorSink.Null); + ast = Parser.CreateParser(context, EmptyOptions).ParseFile(false); + } catch (Exception) { + } + nodes.Add(ast); + } + long start2 = sw.ElapsedMilliseconds; + Console.WriteLine("Parse: {0} ms", start2 - start1); + + for (int i = 0; i < modules.Count; i++) { + var ast = nodes[i]; + + if (ast != null) { + modules[i].UpdateTree(ast, null); + } + } + + long start3 = sw.ElapsedMilliseconds; + for (int i = 0; i < modules.Count; i++) { + Console.WriteLine("Analyzing {1}: {0} ms", sw.ElapsedMilliseconds - start3, sourceUnits[i].Path); + var ast = nodes[i]; + if (ast != null) { + modules[i].Analyze(); + } + } + long start4 = sw.ElapsedMilliseconds; + Console.WriteLine("Analyze: {0} ms", start4 - start3); + Console.ReadLine(); + } + + internal sealed class FileTextContentProvider : TextContentProvider { + private readonly FileStreamContentProvider _provider; + + public FileTextContentProvider(FileStreamContentProvider fileStreamContentProvider) { + _provider = fileStreamContentProvider; + } + + public override SourceCodeReader GetReader() { + return new SourceCodeReader(new StreamReader(_provider.GetStream(), Encoding.ASCII), Encoding.ASCII); + } + } + + internal sealed class FileStreamContentProvider : StreamContentProvider { + private readonly string _path; + + internal string Path { + get { return _path; } + } + + #region Construction + + internal FileStreamContentProvider(string path) { + _path = path; + } + + #endregion + + public override Stream GetStream() { + return new FileStream(_path, FileMode.Open, FileAccess.Read, FileShare.Read); + } + } + + + private static void CollectFiles(string dir, List files) { + foreach (string file in Directory.GetFiles(dir)) { + if (file.EndsWith(".py", StringComparison.OrdinalIgnoreCase)) { + files.Add(file); + } + } + foreach (string nestedDir in Directory.GetDirectories(dir)) { + CollectFiles(nestedDir, files); + } + } + } +} diff --git a/Tools/IronStudio/AnalysisTest/AnalysisTest.cs b/Tools/IronStudio/AnalysisTest/AnalysisTest.cs new file mode 100644 index 0000000000..d615189992 --- /dev/null +++ b/Tools/IronStudio/AnalysisTest/AnalysisTest.cs @@ -0,0 +1,2217 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Numerics; +using System.Text; +using IronPython.Hosting; +using IronPython.Runtime; +using IronPython.Runtime.Types; +using Microsoft.IronPythonTools.Intellisense; +using Microsoft.PyAnalysis; +using Microsoft.PyAnalysis.Values; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Hosting.Providers; +using Microsoft.Scripting.Library; +using Microsoft.Scripting.Utils; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using System.Diagnostics; +using IronPython.Runtime.Exceptions; + +namespace AnalysisTest { + [TestClass] + public partial class AnalysisTest { + private static ScriptEngine _engine; + private static PythonType PyObjectType, IntType, StringType, FloatType, TypeType, ListType, TupleType, BoolType, FunctionType, ComplexType, GeneratorType; + private static string[] _objectMembers, _objectMembersClr, _functionMembers; + private static string[] _strMembers, _strMembersClr; + private static string[] _listMembers, _intMembers; + + public static int Main(string[] args) { + AnalysisTest test = new AnalysisTest(); + + if (args.Length > 0 && args[0] == "PERF") { + args = ArrayUtils.ShiftLeft(args, 1); + return test.RunTests(args, typeof(PerfMethodAttribute)); + } + + return test.RunTests(args, typeof(TestMethodAttribute)); + } + + private int RunTests(string[] args, Type testAttr) { + var fg = Console.ForegroundColor; + int failures = 0; + foreach (var mi in typeof(AnalysisTest).GetMethods()) { + if ((args.Length == 0 || (args.Length > 0 && args.Contains(mi.Name))) && + mi.IsDefined(testAttr, false)) { + + try { + mi.Invoke(this, new object[0]); + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine("Test passed: {0}", mi.Name); + } catch (Exception e) { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("Test failed: {0}", mi.Name); + Console.ForegroundColor = ConsoleColor.Gray; + Console.WriteLine(e); + failures++; + } + } + } + + Console.WriteLine(); + if (failures == 0) { + Console.ForegroundColor = ConsoleColor.Green; + Console.WriteLine("No failures"); + } else { + Console.ForegroundColor = ConsoleColor.Red; + Console.WriteLine("{0} failures", failures); + } + Console.ForegroundColor = fg; + return failures; + } + + #region Test Cases + + [TestMethod] + public void TestImportStar() { + var entry = ProcessText(@" +from nt import * + "); + + var members = entry.GetMembers("", 1).Select(x => x.Name); + + AssertContains(members, "abort"); + + entry = ProcessText(@""); + + // make sure abort hasn't become a builtin, if so this test needs to be updated + // with a new name + members = entry.GetMembers("", 1).Select(x => x.Name); + foreach (var member in members) { + if(member == "abort") { + Assert.Fail("abort has become a builtin, or a clean module includes it for some reason"); + } + } + } + + [TestMethod] + public void TestMutatingReferences() { + var state = new ProjectState(_engine); + + var text1 = @" +import mod2 + +class C(object): + def SomeMethod(self): + pass + +mod2.D(C()) +"; + + var text2 = @" +class D(object): + def __init__(self, value): + self.value = value + self.value.SomeMethod() +"; + + var mod1 = ParseText(state, GetSourceUnit(text1, "mod1"), "mod1"); + var mod2 = ParseText(state, GetSourceUnit(text2, "mod2"), "mod2"); + + VerifyReferences(mod1.Analysis.GetVariables("SomeMethod", GetLineNumber(text1, "SomeMethod")), + new VariableLocation(5, 5, VariableType.Definition), new VariableLocation(5, 9, VariableType.Reference)); + + // mutate 1st file + text1 = text1.Substring(0, text1.IndexOf(" def")) + Environment.NewLine + text1.Substring(text1.IndexOf(" def")); + Prepare(mod1, GetSourceUnit(text1, "mod1")); + mod1.Analyze(); + + VerifyReferences(mod1.Analysis.GetVariables("SomeMethod", GetLineNumber(text1, "SomeMethod")), + new VariableLocation(6, 5, VariableType.Definition), new VariableLocation(5, 9, VariableType.Reference)); + + // mutate 2nd file + text2 = Environment.NewLine + text2; + Prepare(mod2, GetSourceUnit(text2, "mod1")); + mod2.Analyze(); + + VerifyReferences(mod1.Analysis.GetVariables("SomeMethod", GetLineNumber(text1, "SomeMethod")), + new VariableLocation(6, 5, VariableType.Definition), new VariableLocation(6, 9, VariableType.Reference)); + + } + + /// + /// Verify importing wpf will add a reference to the WPF assemblies + /// + [TestMethod] + public void TestWpfReferences() { + var entry = ProcessText(@" +import wpf +from System.Windows.Media import Colors +"); + + AssertContains(entry.GetMembersFromName("Colors", 1), "Blue"); + } + + [TestMethod] + public void TestGenerator() { + var entry = ProcessText(@" +def f(): + yield 1 + yield 2 + yield 3 + +a = f() +b = a.next() + +for c in f(): + print c + "); + + AssertContainsExactly(entry.GetTypesFromName("a", 1), GeneratorType); + AssertContainsExactly(entry.GetTypesFromName("b", 1), IntType); + AssertContainsExactly(entry.GetTypesFromName("c", 1), IntType); + + var text = @" +def f(): + yield 1 + x = yield 2 + +a = f() +b = a.next() +c = a.send('abc')"; + entry = ProcessText(text); + + AssertContainsExactly(entry.GetTypesFromName("a", 1), GeneratorType); + AssertContainsExactly(entry.GetTypesFromName("b", 1), IntType); + AssertContainsExactly(entry.GetTypesFromName("c", 1), IntType); + AssertContainsExactly(entry.GetTypesFromName("x", GetLineNumber(text, "yield 2")), StringType); + } + + [TestMethod] + public void TestEnum() { + var entry = ProcessText(@" +import System +x = System.StringComparison.OrdinalIgnoreCase + "); + + var x = entry.GetValues("x", 1).First(); + Assert.AreEqual(x.ResultType, ResultType.EnumInstance); + } + + /* + [TestMethod] + public void TestListComprehensions() { + var entry = ProcessText(@" +x = [2,3,4] +y = [a for a in x] +z = y[0] + "); + + AssertContainsExactly(entry.GetTypesFromName("z", 0), IntType); + }*/ + + [TestMethod] + public void TestForSequence() { + var entry = ProcessText(@" +x = [('abc', 42, True), ('abc', 23, False),] +for some_str, some_int, some_bool in x: + print some_str + print some_int + print some_bool +"); + AssertContainsExactly(entry.GetTypesFromName("some_str", 1), StringType); + AssertContainsExactly(entry.GetTypesFromName("some_int", 1), IntType); + AssertContainsExactly(entry.GetTypesFromName("some_bool", 1), BoolType); + } + + [TestMethod] + public void TestDynamicAttributes() { + var entry = ProcessText(@" +class x(object): + def __getattr__(self, name): + return 42 + def f(self): + return 'abc' + +a = x().abc +b = x().f() + +class y(object): + def __getattribute__(self, x): + return 'abc' + +c = y().abc +"); + + AssertContainsExactly(entry.GetTypesFromName("a", 1), IntType); + AssertContainsExactly(entry.GetTypesFromName("b", 1), StringType); + AssertContainsExactly(entry.GetTypesFromName("c", 1), StringType); + } + + [TestMethod] + public void TestListAppend() { + var entry = ProcessText(@" +x = [] +x.append('abc') +y = x[0] +"); + + AssertContainsExactly(entry.GetTypesFromName("y", 1), StringType); + + entry = ProcessText(@" +x = [] +x.extend(('abc', )) +y = x[0] +"); + AssertContainsExactly(entry.GetTypesFromName("y", 1), StringType); + + entry = ProcessText(@" +x = [] +x.insert(0, 'abc') +y = x[0] +"); + AssertContainsExactly(entry.GetTypesFromName("y", 1), StringType); + + entry = ProcessText(@" +x = [] +x.append('abc') +y = x.pop() +"); + + AssertContainsExactly(entry.GetTypesFromName("y", 1), StringType); + + entry = ProcessText(@" +class ListTest(object): + def reset(self): + self.items = [] + self.pushItem(self) + def pushItem(self, item): + self.items.append(item) + +a = ListTest() +b = a.items[0]"); + + AssertContains(entry.GetMembersFromName("b", 1), "pushItem"); + } + + [TestMethod] + public void TestSlicing() { + var entry = ProcessText(@" +x = [2] +y = x[:-1] +z = y[0] +"); + + AssertContainsExactly(entry.GetTypesFromName("z", 1), IntType); + + entry = ProcessText(@" +x = (2, 3, 4) +y = x[:-1] +z = y[0] +"); + + AssertContainsExactly(entry.GetTypesFromName("z", 1), IntType); + } + + [TestMethod] + public void TestColor() { + + var entry = ProcessText(@" +import clr +clr.AddReference('PresentationFramework') +clr.AddReference('PresentationCore') + +from System.Windows.Media import Colors + +class C(object): + def __init__(self): + if False: + self.some_color = Colors.Black + else: + self.some_color = Colors.White + + +a = C() +b = a.some_color +"); + + AssertContainsExactly(entry.GetTypesFromName("b", 1).Select(x => PythonType.Get__name__(x)), "Color"); + } + + [TestMethod] + public void TestConstantIndex() { + var entry = ProcessText(@" +ZERO = 0 +ONE = 1 +TWO = 2 +x = ['abc', 42, True)] + + +some_str = x[ZERO] +some_int = x[ONE] +some_bool = x[TWO] +"); + AssertContainsExactly(entry.GetTypesFromName("some_str", 1), StringType); + AssertContainsExactly(entry.GetTypesFromName("some_int", 1), IntType); + AssertContainsExactly(entry.GetTypesFromName("some_bool", 1), BoolType); + } + [TestMethod] + public void TestCtorSignatures() { + var entry = ProcessText(@" +class C: pass + +class D(object): pass + +class E(object): + def __init__(self): pass + +class F(object): + def __init__(self, one): pass + +class G(object): + def __new__(cls): pass + +class H(object): + def __new__(cls, one): pass + + "); + + var result = entry.GetSignatures("C", 1).ToArray(); + Assert.AreEqual(result.Length, 1); + Assert.AreEqual(result[0].Parameters.Length, 0); + + result = entry.GetSignatures("D", 1).ToArray(); + Assert.AreEqual(result.Length, 1); + Assert.AreEqual(result[0].Parameters.Length, 0); + + result = entry.GetSignatures("E", 1).ToArray(); + Assert.AreEqual(result.Length, 1); + Assert.AreEqual(result[0].Parameters.Length, 0); + + result = entry.GetSignatures("F", 1).ToArray(); + Assert.AreEqual(result.Length, 1); + Assert.AreEqual(result[0].Parameters.Length, 1); + + result = entry.GetSignatures("G", 1).ToArray(); + Assert.AreEqual(result.Length, 1); + Assert.AreEqual(result[0].Parameters.Length, 0); + + result = entry.GetSignatures("H", 1).ToArray(); + Assert.AreEqual(result.Length, 1); + Assert.AreEqual(result[0].Parameters.Length, 1); + } + + [TestMethod] + public void TestBuiltinTypeSignatures() { + var entry = ProcessText(@" +import System +x = str +x = int + +y = str +y = int +"); + + var result = entry.GetSignatures("System.Collections.Generic.Dictionary[int, int]", 1).ToArray(); + Assert.AreEqual(result.Length, 6); + + // 2 possible types + result = entry.GetSignatures("System.Collections.Generic.Dictionary[x, int]", 1).ToArray(); + Assert.AreEqual(result.Length, 12); + + // 4 possible types + result = entry.GetSignatures("System.Collections.Generic.Dictionary[x, y]", 1).ToArray(); + Assert.AreEqual(result.Length, 24); + } + + [TestMethod] + public void TestBuiltinMethodSignatures() { + var entry = ProcessText(@" +const = """".capitalize +constructed = str().capitalize +"); + + string[] testCapitalize = new[] { "const", "constructed" }; + foreach (var test in testCapitalize) { + var result = entry.GetSignatures(test, 1).ToArray(); + Assert.AreEqual(result.Length, 1); + Assert.AreEqual(result[0].Parameters.Length, 0); + } + + entry = ProcessText(@" +import clr +const = """".Contains +constructed = str().Contains +"); + + string[] testContains = new[] { "const", "constructed" }; + foreach (var test in testContains) { + var result = entry.GetSignatures(test, 1).ToArray(); + Assert.AreEqual(result.Length, 1); + Assert.AreEqual(result[0].Parameters.Length, 1); + Assert.AreEqual(result[0].Parameters[0].Name, "value"); + Assert.AreEqual(result[0].Parameters[0].IsOptional, false); + } + } + + [TestMethod] + public void TestDel() { + string text = @" +del foo +del foo[2] +del foo.bar +del (foo) +del foo, bar +"; + var entry = ProcessText(text); + + // We do no analysis on del statements, nothing to test + } + + [TestMethod] + public void TryExcept() { + string text = @" +class MyException(Exception): pass + +def f(): + try: + except TypeError, e1: + pass + +def g(): + try: + except MyException, e2: + pass +"; + var entry = ProcessText(text); + + AssertContainsExactly(GetTypes(entry.GetValues("e1", GetLineNumber(text, ", e1"))), PythonExceptions.TypeError); + + AssertContainsExactly(GetTypeNames(entry.GetValues("e2", GetLineNumber(text, ", e2"))), "MyException instance"); + } + + private IEnumerable GetTypes(IEnumerable analysisValues) { + foreach (var value in analysisValues) { + yield return value.PythonType; + } + } + + private IEnumerable GetTypeNames(IEnumerable analysisValues) { + foreach (var value in analysisValues) { + yield return value.ShortDescription; + } + } + + class VariableLocation { + public readonly int StartLine; + public readonly int StartCol; + public readonly VariableType Type; + + public VariableLocation(int startLine, int startCol, VariableType type) { + StartLine = startLine; + StartCol = startCol; + Type = type; + } + } + + [TestMethod] + public void TestReferences() { + // instance variables + var text = @" +# add ref w/o type info +class C(object): + def __init__(self, foo): + self.abc = foo + del self.abc + print self.abc + +"; + var entry = ProcessText(text); + VerifyReferences(entry.GetVariables("self.abc", GetLineNumber(text, "self.abc")), new VariableLocation(5, 9, VariableType.Definition), new VariableLocation(6, 13, VariableType.Reference), new VariableLocation(7, 15, VariableType.Reference)); + VerifyReferences(entry.GetVariables("foo", GetLineNumber(text, "foo")), new VariableLocation(4, 24, VariableType.Definition), new VariableLocation(5, 20, VariableType.Reference)); + + text = @" +# add ref w/ type info +class D(object): + def __init__(self, foo): + self.abc = foo + del self.abc + print self.abc + +D(42)"; + entry = ProcessText(text); + + VerifyReferences(entry.GetVariables("self.abc", GetLineNumber(text, "self.abc")), new VariableLocation(5, 9, VariableType.Definition), new VariableLocation(6, 13, VariableType.Reference), new VariableLocation(7, 15, VariableType.Reference)); + VerifyReferences(entry.GetVariables("foo", GetLineNumber(text, "foo")), new VariableLocation(4, 24, VariableType.Definition), new VariableLocation(5, 20, VariableType.Reference)); + VerifyReferences(entry.GetVariables("D", GetLineNumber(text, "D(42)")), new VariableLocation(9, 1, VariableType.Reference), new VariableLocation(3, 1, VariableType.Definition)); + + // function definitions + text = @" +def f(): pass + +x = f()"; + entry = ProcessText(text); + VerifyReferences(entry.GetVariables("f", GetLineNumber(text, "x =")), new VariableLocation(4, 5, VariableType.Reference), new VariableLocation(2, 1, VariableType.Definition)); + + text = @" +from System import EventHandler +def g(): + x = EventHandler(f) + +def f(sender, args): pass +"; + entry = ProcessText(text); + VerifyReferences(entry.GetVariables("f", GetLineNumber(text, "x =")), new VariableLocation(4, 22, VariableType.Reference), new VariableLocation(6, 1, VariableType.Definition)); + + text = @" +from System import EventHandler +def f(sender, args): pass + +x = EventHandler(f)"; + entry = ProcessText(text); + VerifyReferences(entry.GetVariables("f", GetLineNumber(text, "x =")), new VariableLocation(5, 18, VariableType.Reference), new VariableLocation(3, 1, VariableType.Definition)); + + // left hand side is unknown, right hand side should still have refs added + text = @" +from System import EventHandler +def f(sender, args): pass + +a.foo += EventHandler(f) +"; + entry = ProcessText(text); + VerifyReferences(entry.GetVariables("f", GetLineNumber(text, "a.foo +=")), new VariableLocation(5, 23, VariableType.Reference), new VariableLocation(3, 1, VariableType.Definition)); + + + text = @" +def f(): pass + +x = f"; + entry = ProcessText(text); + VerifyReferences(entry.GetVariables("f", GetLineNumber(text, "x =")), new VariableLocation(4, 5, VariableType.Reference), new VariableLocation(2, 1, VariableType.Definition)); + + // class variables + text = @" + +class D(object): + abc = 42 + print abc + del abc +"; + entry = ProcessText(text); + + VerifyReferences(entry.GetVariables("abc", GetLineNumber(text, "abc =")), new VariableLocation(4, 5, VariableType.Definition), new VariableLocation(5, 11, VariableType.Reference), new VariableLocation(6, 9, VariableType.Reference)); + + // class definition + text = @" +class D(object): pass + +a = D +"; + entry = ProcessText(text); + VerifyReferences(entry.GetVariables("D", GetLineNumber(text, "a =")), new VariableLocation(4, 5, VariableType.Reference), new VariableLocation(2, 1, VariableType.Definition)); + + // method definition + text = @" +class D(object): + def f(self): pass + +a = D().f() +"; + entry = ProcessText(text); + VerifyReferences(entry.GetVariables("D().f", GetLineNumber(text, "a =")), + new VariableLocation(5, 5, VariableType.Reference), new VariableLocation(3, 5, VariableType.Definition)); + + // globals + text = @" +abc = 42 +print abc +del abc +"; + entry = ProcessText(text); + VerifyReferences(entry.GetVariables("abc", GetLineNumber(text, "abc =")), new VariableLocation(4, 5, VariableType.Reference), new VariableLocation(2, 1, VariableType.Definition), new VariableLocation(3, 7, VariableType.Reference)); + + // parameters + text = @" +def f(abc): + print abc + abc = 42 + del abc +"; + entry = ProcessText(text); + VerifyReferences(entry.GetVariables("abc", GetLineNumber(text, "abc =")), new VariableLocation(2, 7, VariableType.Definition), new VariableLocation(4, 5, VariableType.Definition), new VariableLocation(3, 11, VariableType.Reference), new VariableLocation(5, 9, VariableType.Reference)); + + + // grammer test - statements + text = @" +def f(abc): + try: pass + except abc: pass + + try: pass + except TypeError, abc: pass + + abc, bar = 42, 23 + abc[23] = 42 + abc.foo = 42 + abc += 2 + + class D(abc): pass + + for x in abc: print x + + import abc + from xyz import abc + from xyz import bar as abc + + if abc: print 'hi' + elif abc: print 'bye' + else: abc + + with abc: + return abc + + print abc + assert abc, abc + + raise abc + raise abc, abc, abc + + while abc: + abc + else: + abc + + for x in foo: + print x + else: + print abc + + try: pass + except TypeError: + else: + abc +"; + entry = ProcessText(text); + VerifyReferences(entry.GetVariables("abc", GetLineNumber(text, "f(abc)")), + new VariableLocation(2, 7, VariableType.Definition), + new VariableLocation(4, 12, VariableType.Reference), + new VariableLocation(7, 23, VariableType.Definition), + + new VariableLocation(9, 5, VariableType.Definition), + new VariableLocation(10, 5, VariableType.Reference), + new VariableLocation(11, 5, VariableType.Reference), + new VariableLocation(12, 5, VariableType.Reference), + + new VariableLocation(14, 13, VariableType.Reference), + + new VariableLocation(16, 14, VariableType.Reference), + + new VariableLocation(18, 5, VariableType.Reference), + new VariableLocation(19, 5, VariableType.Reference), + new VariableLocation(20, 5, VariableType.Reference), + + new VariableLocation(22, 8, VariableType.Reference), + new VariableLocation(23, 10, VariableType.Reference), + new VariableLocation(24, 11, VariableType.Reference), + + new VariableLocation(26, 10, VariableType.Reference), + new VariableLocation(27, 16, VariableType.Reference), + + new VariableLocation(29, 11, VariableType.Reference), + new VariableLocation(30, 12, VariableType.Reference), + new VariableLocation(30, 17, VariableType.Reference), + + new VariableLocation(32, 11, VariableType.Reference), + new VariableLocation(33, 11, VariableType.Reference), + new VariableLocation(33, 16, VariableType.Reference), + new VariableLocation(33, 21, VariableType.Reference), + + new VariableLocation(35, 11, VariableType.Reference), + new VariableLocation(36, 9, VariableType.Reference), + new VariableLocation(38, 9, VariableType.Reference), + + new VariableLocation(43, 15, VariableType.Reference), + + new VariableLocation(48, 9, VariableType.Reference) + ); + + + // grammer test - expressions + text = @" +def f(abc): + x = abc + 2 + x = 2 + abc + x = l[abc] + x = abc[l] + x = abc.foo + + g(abc) + + abc if abc else abc + + {abc:abc}, + [abc, abc] + (abc, abc) + {abc} + + yield abc + [x for x in abc] + (x for x in abc) + + abc or abc + abc and abc + + +abc + x[abc:abc:abc] + + abc == abc + not abc + + lambda : abc +"; + entry = ProcessText(text); + VerifyReferences(entry.GetVariables("abc", GetLineNumber(text, "f(abc)")), + new VariableLocation(2, 7, VariableType.Definition), + + new VariableLocation(3, 9, VariableType.Reference), + new VariableLocation(4, 13, VariableType.Reference), + + new VariableLocation(5, 10, VariableType.Reference), // BUGBUG: should be 5,11 + new VariableLocation(6, 9, VariableType.Reference), + new VariableLocation(7, 9, VariableType.Reference), + new VariableLocation(9, 7, VariableType.Reference), + + new VariableLocation(11, 5, VariableType.Reference), + new VariableLocation(11, 12, VariableType.Reference), + new VariableLocation(11, 21, VariableType.Reference), + + new VariableLocation(13, 6, VariableType.Reference), + new VariableLocation(13, 10, VariableType.Reference), + new VariableLocation(14, 6, VariableType.Reference), + new VariableLocation(14, 11, VariableType.Reference), + new VariableLocation(15, 6, VariableType.Reference), + new VariableLocation(15, 11, VariableType.Reference), + new VariableLocation(16, 6, VariableType.Reference), + + new VariableLocation(18, 11, VariableType.Reference), + new VariableLocation(19, 17, VariableType.Reference), + new VariableLocation(20, 17, VariableType.Reference), + + new VariableLocation(22, 5, VariableType.Reference), + new VariableLocation(22, 12, VariableType.Reference), + new VariableLocation(23, 5, VariableType.Reference), + new VariableLocation(23, 13, VariableType.Reference), + + new VariableLocation(25, 6, VariableType.Reference), + new VariableLocation(26, 7, VariableType.Reference), + new VariableLocation(26, 11, VariableType.Reference), + new VariableLocation(26, 15, VariableType.Reference), + + new VariableLocation(28, 5, VariableType.Reference), + new VariableLocation(28, 12, VariableType.Reference), + new VariableLocation(29, 9, VariableType.Reference) + + //new VariableLocation(30, 14, VariableType.Reference) // BUGBUG: Enable when lambda bodies are walked + ); + } + + private void VerifyReferences(IEnumerable variables, params VariableLocation[] variableType) { + var vars = new List(variables); + if (vars.Count == 0) { + Assert.Fail("Got no references"); + } + + int removed = 0; + bool removedOne = false; + do { + for (int j = 0; j < variableType.Length; j++) { + var expected = variableType[j]; + + bool found = false; + for (int i = 0; i < vars.Count; i++) { + var have = vars[i]; + + if (have.Location.Line == expected.StartLine && + have.Location.Column == expected.StartCol && + have.Type == expected.Type) { + vars.RemoveAt(i); + removed++; + removedOne = found = true; + break; + } + } + + if (!found) { + StringBuilder error = new StringBuilder(String.Format("Failed to find location: {0} {1} {2}" + Environment.NewLine, expected.StartLine, expected.StartCol, expected.Type)); + LocationNames(vars, error); + + Assert.Fail(error.ToString()); + } + } + } while (vars.Count != 0 && removedOne); + + if (vars.Count != 0) { + StringBuilder error = new StringBuilder("Didn't use all locations - had " + variables.Count() + Environment.NewLine); + LocationNames(vars, error); + Assert.Fail(error.ToString()); + } + } + + private static void LocationNames(List vars, StringBuilder error) { + foreach (var var in vars) { + error.AppendFormat(" {0} {1} {2}", var.Location.Line, var.Location.Column, var.Type); + error.AppendLine(); + } + } + + [TestMethod] + public void TestSignatureDefaults() { + var entry = ProcessText(@" +def f(x = None): pass + +def g(x = {}): pass + +def h(x = {2:3}): pass + +def i(x = []): pass + +def j(x = [None]): pass + +def k(x = ()): pass + +def l(x = (2, )): pass +"); + + var tests = new[] { + new { FuncName = "f", ParamName="x = None" }, + new { FuncName = "g", ParamName="x = {}" }, + new { FuncName = "h", ParamName="x = {...}" }, + new { FuncName = "i", ParamName="x = []" }, + new { FuncName = "j", ParamName="x = [...]" }, + new { FuncName = "k", ParamName="x = ()" }, + new { FuncName = "l", ParamName="x = (...)" }, + }; + + foreach (var test in tests) { + var result = entry.GetSignatures(test.FuncName, 1).ToArray(); + Assert.AreEqual(result.Length, 1); + Assert.AreEqual(result[0].Parameters[0].Name, test.ParamName); + } + } + + [TestMethod] + public void TestGetVariablesDictionaryGet() { + var entry = ProcessText(@" +x = {42:'abc'} + "); + + foreach (var varRef in entry.GetValues("x.get", 1)) { + Assert.AreEqual("built-in method get", varRef.Description); + } + } + + [TestMethod] + public void TestLambdaExpression() { + var entry = ProcessText(@" +x = lambda a: a +y = x(42) +"); + + AssertContainsExactly(entry.GetTypesFromName("y", 1), IntType); + + entry = ProcessText(@" +def f(a): + return a + +x = lambda b: f(b) +y = x(42) +"); + + AssertContainsExactly(entry.GetTypesFromName("y", 1), IntType); + } + + [TestMethod] + public void TestRecursiveClass() { + var entry = ProcessText(@" +cls = object + +class cls(cls): + abc = 42 +"); + + entry.GetMembersFromName("cls", 1); + AssertContainsExactly(entry.GetMembers("cls().abc.", 1).Select(member => member.Name), _intMembers); + AssertContainsExactly(entry.GetMembers("cls.abc.", 1).Select(member => member.Name), _intMembers); + } + + [TestMethod] + public void TestBadMethod() { + var entry = ProcessText(@" +class cls(object): + def f(): + return 42 + +abc = cls() +foo = abc.f() +"); + + AssertContainsExactly(entry.GetMembers("foo.", 1).Select(member => member.Name), _intMembers); + } + + [TestMethod] + public void TestKeywordArguments() { + var funcDef = "def f(a, b, c): pass"; + var classWithInit = @"class f(object): + def __init__(self, a, b, c): + pass"; + var classWithNew = @"class f(object): + def __new__(cls, a, b, c): + pass"; + var method = @"class x(object): + def g(self, a, b, c): + pass + +f = x().g"; + var decls = new [] { funcDef, classWithInit, classWithNew, method }; + + foreach (var decl in decls) { + string[] testCalls = new[] { + "f(c = 'abc', b = 42, a = 3j)", "f(3j, c = 'abc', b = 42)", "f(3j, 42, c = 'abc')", + "f(c = 'abc', b = 42, a = 3j, d = 42)", // extra argument + "f(3j, 42, 'abc', d = 42)", + }; + + foreach (var testCall in testCalls) { + var text = decl + Environment.NewLine + testCall; + var entry = ProcessText(text); + + AssertContainsExactly(entry.GetTypesFromName("a", GetLineNumber(text, "pass")), ComplexType); + AssertContainsExactly(entry.GetTypesFromName("b", GetLineNumber(text, "pass")), IntType); + AssertContainsExactly(entry.GetTypesFromName("c", GetLineNumber(text, "pass")), StringType); + } + } + } + + [TestMethod] + public void TestPositionalSplat() { + var funcDef = "def f(a, b, c): pass"; + var classWithInit = @"class f(object): + def __init__(self, a, b, c): + pass"; + var classWithNew = @"class f(object): + def __new__(cls, a, b, c): + pass"; + var method = @"class x(object): + def g(self, a, b, c): + pass + +f = x().g"; + var decls = new[] { funcDef, classWithInit, classWithNew, method }; + + foreach (var decl in decls) { + string[] testCalls = new[] { + "f(*(3j, 42, 'abc'))", + "f(*[3j, 42, 'abc'])", + "f(*(3j, 42, 'abc', 4L))", // extra argument + "f(*[3j, 42, 'abc', 4L])", // extra argument + }; + + foreach (var testCall in testCalls) { + var text = decl + Environment.NewLine + testCall; + var entry = ProcessText(text); + + AssertContainsExactly(entry.GetTypesFromName("a", GetLineNumber(text, "pass")), ComplexType); + AssertContainsExactly(entry.GetTypesFromName("b", GetLineNumber(text, "pass")), IntType); + AssertContainsExactly(entry.GetTypesFromName("c", GetLineNumber(text, "pass")), StringType); + } + } + } + [TestMethod] + public void TestForwardRef() { + var text = @" + +class D(object): + def bar(self, x): + abc = C() + abc.foo(2) + a = abc.foo(2.0) + a.bar(('a', 'b', 'c', 'd')) + +class C(object): + def foo(self, x): + D().bar('abc') + D().bar(['a', 'b', 'c']) + return D() + def baz(self): pass +"; + var entry = ProcessText(text); + + var fifty = entry.GetVariablesNoBuiltins(GetLineNumber(text, "abc.foo")).ToSet(); + AssertContainsExactly(fifty, "C", "D", "a", "abc", "self", "x"); + + var three = entry.GetVariablesNoBuiltins(GetLineNumber(text, "lass D")).ToSet(); + AssertContainsExactly(three, "C", "D", "bar"); + + var allFifty = entry.GetMembersFromName("abc", GetLineNumber(text, "abc.foo")).ToSet(); + AssertContainsExactly(allFifty, GetUnion(_objectMembers, "baz", "foo")); + + var xTypes = entry.GetTypesFromName("x", GetLineNumber(text, "abc.foo")).ToSet(); + AssertContainsExactly(xTypes, ListType, StringType, TupleType); + + var xMembers = entry.GetMembersFromName("x", GetLineNumber(text, "abc.foo")).ToSet(); + AssertContainsExactly(xMembers, GetIntersection(_strMembers, _listMembers)); + } + + private static int GetLineNumber(string text, string substring) { + string[] splitLines = text.Split('\n'); + for (int i = 0; i < splitLines.Length; i++) { + if (splitLines[i].IndexOf(substring) != -1) { + return i + 1; + } + } + + throw new InvalidOperationException(); + } + + [TestMethod] + public void TestBuiltins() { + var text = @" +booltypetrue = True +booltypefalse = False +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("booltypetrue", 1), BoolType); + AssertContainsExactly(entry.GetTypesFromName("booltypefalse", 1), BoolType); + } + + [TestMethod] + public void TestDictionaryFunctionTable() { + var text = @" +def f(a, b): + print a, b + +def g(a, b): + x, y = a, b + +x = {'foo': f, 'bar' : g} +x['foo'](42, []) +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("a", GetLineNumber(text, "print")), IntType); + AssertContainsExactly(entry.GetTypesFromName("b", GetLineNumber(text, "print")), ListType); + AssertContainsExactly(entry.GetTypesFromName("a", GetLineNumber(text, "x, y")), IntType); + AssertContainsExactly(entry.GetTypesFromName("b", GetLineNumber(text, "x, y")), ListType); + } + + [TestMethod] + public void TestDictionaryAssign() { + var text = @" +x = {'abc': 42} +y = x['foo'] +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("y", 1), IntType); + } + + [TestMethod] + public void TestDictionaryFunctionTableGet2() { + var text = @" +def f(a, b): + print a, b + +def g(a, b): + x, y = a, b + +x = {'foo': f, 'bar' : g} +x.get('foo')(42, []) +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("a", GetLineNumber(text, "print")), IntType); + AssertContainsExactly(entry.GetTypesFromName("b", GetLineNumber(text, "print")), ListType); + AssertContainsExactly(entry.GetTypesFromName("a", GetLineNumber(text, "x, y")), IntType); + AssertContainsExactly(entry.GetTypesFromName("b", GetLineNumber(text, "x, y")), ListType); + } + + [TestMethod] + public void TestDictionaryFunctionTableGet() { + var text = @" +def f(a, b): + print a, b + +def g(a, b): + x, y = a, b + +x = {'foo': f, 'bar' : g} +y = x.get('foo', None) +if y is not None: + y(42, []) +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("a", GetLineNumber(text, "print")), IntType); + AssertContainsExactly(entry.GetTypesFromName("b", GetLineNumber(text, "print")), ListType); + AssertContainsExactly(entry.GetTypesFromName("a", GetLineNumber(text, "x, y")), IntType); + AssertContainsExactly(entry.GetTypesFromName("b", GetLineNumber(text, "x, y")), ListType); + } + + [TestMethod] + public void TestSimpleGlobals() { + var text = @" +class x(object): + def abc(self): + pass + +a = x() +x.abc() +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetVariablesNoBuiltins(1), "a", "x"); + AssertContainsExactly(entry.GetMembersFromName("x", 1), GetUnion(_objectMembers, "abc")); + } + + [TestMethod] + public void TestFuncCallInIf() { + var text = @" +def Method(a, b, c): + print a, b, c + +if not Method(42, 'abc', []): + pass +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("a", GetLineNumber(text, "print")), IntType); + AssertContainsExactly(entry.GetTypesFromName("b", GetLineNumber(text, "print")), StringType); + AssertContainsExactly(entry.GetTypesFromName("c", GetLineNumber(text, "print")), ListType); + } + + [TestMethod] + public void TestWithStatement() { + var text = @" +class x(object): + def x_method(self): + pass + +with x() as foo: + print foo + +with x(): + pass +"; + var entry = ProcessText(text); + var foo = entry.GetMembersFromName("foo", GetLineNumber(text, "print foo")); + AssertContainsExactly(foo, GetUnion(_objectMembers, "x_method")); + } + + [TestMethod] + public void TestOverrideFunction() { + var text = @" +class bar(object): + def Call(self, xvar, yvar): + pass + +class baz(bar): + def Call(self, xvar, yvar): + pass + +class Cxxxx(object): + def __init__(self): + self.foo = baz() + + def Cmeth(self, avar, bvar): + self.foo.Call(avar, bvar) + + + +abc = Cxxxx() +abc.Cmeth(['foo'], 'bar') +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("xvar", GetLineNumber(text, "pass")), ListType); + } + + [TestMethod] + public void TestSimpleMethodCall() { + var text = @" +class x(object): + def abc(self, foo): + pass + +a = x() +a.abc('abc') +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("foo", GetLineNumber(text, "pass")), StringType); + AssertContainsExactly(entry.GetMembersFromName("self", GetLineNumber(text, "pass")), GetUnion(_objectMembers, "abc")); + } + + [TestMethod] + public void TestSystemFromImport() { + var text = @" +from System import Environment +Environment.GetCommandLineArgs() +"; + var entry = ProcessText(text); + Assert.IsTrue(entry.GetMembersFromName("Environment", 1).Any(s => s == "CommandLine")); + } + + [TestMethod] + public void TestImportAs() { + var text = @" +import System.Collections as coll +"; + var entry = ProcessText(text); + Assert.IsTrue(entry.GetMembersFromName("coll", 1).Any(s => s == "ArrayList")); + } + + [TestMethod] + public void TestSystemImport() { + var text = @" +import System +System.Environment.GetCommandLineArgs() +x = System.Environment +"; + var entry = ProcessText(text); + var system = entry.GetMembersFromName("System", 1).ToSet(); + // defined in mscorlib + AssertContains(system, "AccessViolationException"); + // defined in System + AssertContains(system, "CodeDom"); + + AssertContains(entry.GetMembersFromName("x", 1), "GetEnvironmentVariables"); + } + + [TestMethod] + public void TestSystemMembers() { + var text = @" +import System +System.Environment.GetCommandLineArgs() +x = System.Environment +args = x.GetCommandLineArgs() +"; + var entry = ProcessText(text); + + var args = entry.GetTypesFromName("args", GetLineNumber(text, "args =")).ToSet(); + AssertContainsExactly(args, ClrModule.GetPythonType(typeof(string[]))); + + Assert.IsTrue(entry.GetMembersFromName("args", GetLineNumber(text, "args =")).Any(s => s == "AsReadOnly")); + } + + [TestMethod] + public void TestNamespaceMembers() { + var text = @" +import System +x = System.Collections +"; + var entry = ProcessText(text); + var x = entry.GetMembersFromName("x", GetLineNumber(text, "x =")).ToSet(); + Assert.IsTrue(x.Contains("Generic")); + Assert.IsTrue(x.Contains("ArrayList")); + } + + [TestMethod] + public void TestBuiltinRetval() { + var text = @" +x = [2,3,4] +a = x.index(2) +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("x", GetLineNumber(text, "x =")).ToSet(), ListType); + AssertContainsExactly(entry.GetTypesFromName("a", GetLineNumber(text, "a =")).ToSet(), IntType); + } + + [TestMethod] + public void TestBuiltinFuncRetval() { + var text = @" +x = ord('a') +y = range(5) +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("x", GetLineNumber(text, "x = ")).ToSet(), IntType); + AssertContainsExactly(entry.GetTypesFromName("y", GetLineNumber(text, "y = ")).ToSet(), ListType); + } + + [TestMethod] + public void TestFunctionMembers() { + var text = @" +def f(x): pass +f.abc = 32 +"; + var entry = ProcessText(text); + AssertContains(entry.GetMembersFromName("f", 1), "abc"); + + text = @" +def f(x): pass + +"; + entry = ProcessText(text); + AssertDoesntContain(entry.GetMembersFromName("f", 1), "x"); + AssertContainsExactly(entry.GetMembersFromName("f", 1), _functionMembers); + + AssertContainsExactly(entry.GetMembersFromName("f.func_name", 1), _strMembers); + } + + + [TestMethod] + public void TestRangeIteration() { + var text = @" +for i in range(5): + pass +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("i", GetLineNumber(text, "for i")).ToSet(), IntType); + } + + [TestMethod] + public void TestBuiltinImport() { + var text = @" +import sys +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetVariablesNoBuiltins(1), "sys"); + Assert.IsTrue(entry.GetMembersFromName("sys", 1).Any((s) => s == "winver")); + } + + [TestMethod] + public void TestBuiltinImportInFunc() { + var text = @" +def f(): + import sys +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetVariablesNoBuiltins(GetLineNumber(text, "sys")), "f", "sys"); + AssertContains(entry.GetMembersFromName("sys", GetLineNumber(text, "sys")), "winver"); + } + + [TestMethod] + public void TestBuiltinImportInClass() { + var text = @" +class C: + import sys +"; + var entry = ProcessText(text); + + AssertContainsExactly(entry.GetVariablesNoBuiltins(GetLineNumber(text, "sys")), "C", "sys"); + Assert.IsTrue(entry.GetMembersFromName("sys", GetLineNumber(text, "sys")).Any((s) => s == "winver")); + } + + [TestMethod] + public void TestNoImportClr() { + var text = @" +x = 'abc' +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("x", 1), StringType); + AssertContainsExactly(entry.GetMembersFromName("x", 1), _strMembers); + } + + [TestMethod] + public void TestImportClr() { + var text = @" +import clr +x = 'abc' +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetMembersFromName("x", 1), _strMembersClr); + } + + [TestMethod] + public void TestClrAddReference() { + var text = @" +import clr +clr.AddReference('System.Drawing') +from System.Drawing import Point +"; + var entry = ProcessText(text); + Assert.AreEqual(35, entry.GetMembersFromName("Point", GetLineNumber(text, "from System.")).ToList().Count); + } + + [TestMethod] + public void TestClrAddReferenceByName() { + var text = @" +import clr +clr.AddReferenceByName('Microsoft.Scripting') +from Microsoft.Scripting import SourceUnit +"; + var entry = ProcessText(text); + Assert.AreEqual(40, entry.GetMembersFromName("SourceUnit", GetLineNumber(text, "from Microsoft.")).ToList().Count); + } + + [TestMethod] + public void TestMutualRecursion() { + var text = @" +class C: + def f(self, other, depth): + if depth == 0: + return 'abc' + return other.g(self, depth - 1) + +class D: + def g(self, other, depth): + if depth == 0: + return ['d', 'e', 'f'] + + return other.f(self, depth - 1) + +x = D().g(C(), 42) + +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetMembersFromName("other", GetLineNumber(text, "other.g")), "g"); + AssertContainsExactly(entry.GetTypesFromName("x", GetLineNumber(text, "x =")), ListType, StringType); + AssertContainsExactly(entry.GetMembersFromName("x", GetLineNumber(text, "x =")), + GetIntersection(_listMembers, _strMembers)); + } + + [TestMethod] + public void TestForwardRefVars() { + var text = @" +class x(object): + def __init__(self, val): + self.abc = [] + +x(42) +x('abc') +x([]) +"; + var entry = ProcessText(text); + Assert.AreEqual(1, entry.GetValues("self.abc", GetLineNumber(text, "self.abc")).ToList().Count); + } + + [TestMethod] + public void TestReturnFunc() { + var text = @" +def g(): + return [] + +def f(): + return g + +x = f()() +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("x", 1), ListType); + } + + [TestMethod] + public void TestReturnArg() { + var text = @" +def g(a): + return a + +x = g(1) +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("x", 1), IntType); + } + + [TestMethod] + public void TestReturnArg2() { + var text = @" + +def f(a): + def g(): + return a + return g + +x = f(2)() +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("x", 1), IntType); + } + + [TestMethod] + public void TestMemberAssign() { + var text = @" +class C: + def func(self): + self.abc = 42 + +a = C() +a.func() +foo = a.abc +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("foo", 1), IntType); + AssertContainsExactly(entry.GetMembersFromName("foo", 1), _intMembers); + AssertContainsExactly(entry.GetMembersFromName("a", 1), "abc", "func"); + } + + [TestMethod] + public void TestMemberAssign2() { + var text = @" +class D: + def func2(self): + a = C() + a.func() + return a.abc + +class C: + def func(self): + self.abc = [2,3,4] + +foo = D().func2() +"; + var entry = ProcessText(text); + // TODO: AssertContainsExactly(entry.GetTypesFromName("foo", 0), ListType); + } + + [TestMethod] + public void TestUnfinishedDot() { + // the partial dot should be ignored and we shouldn't see g as + // a member of D + var text = @" +class D(object): + def func(self): + self. + +def g(a, b, c): pass +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetMembersFromName("self", GetLineNumber(text, "self")), + GetUnion(_objectMembers, "func")); + } + + [TestMethod] + public void TestCrossModule() { + var text1 = @" +import mod2 +"; + var text2 = @" +x = 42 +"; + + PermutedTest("mod", new[] { text1, text2 }, (pe) => { + AssertContainsExactly(pe[0].Analysis.GetMembersFromName("mod2", 1), "x"); + }); + } + + [TestMethod] + public void TestCrossModuleCall() { + var text1 = @" +import mod2 +y = mod2.f('abc') +"; + var text2 = @" +def f(x): + return x +"; + + PermutedTest("mod", new[] { text1, text2 }, (pe) => { + AssertContainsExactly(pe[1].Analysis.GetTypesFromName("x", GetLineNumber(text2, "return x")), StringType); + AssertContainsExactly(pe[0].Analysis.GetTypesFromName("y", GetLineNumber(text1, "y")), StringType); + }); + } + + [TestMethod] + public void TestCrossModuleCallType() { + var text1 = @" +import mod2 +y = mod2.c('abc').x +"; + var text2 = @" +class c: + def __init__(self, x): + self.x = x +"; + + PermutedTest("mod", new[] { text1, text2 }, (pe) => { + AssertContainsExactly(pe[1].Analysis.GetTypesFromName("x", GetLineNumber(text2, "= x")), StringType); + AssertContainsExactly(pe[0].Analysis.GetTypesFromName("y", GetLineNumber(text1, "y")), StringType); + }); + } + + [TestMethod] + public void TestCrossModuleCallType2() { + var text1 = @" +from mod2 import c +class x(object): + def Foo(self): + y = c('abc').x +"; + var text2 = @" +class c: + def __init__(self, x): + self.x = x +"; + + PermutedTest("mod", new[] { text1, text2 }, (pe) => { + AssertContainsExactly(pe[1].Analysis.GetTypesFromName("x", GetLineNumber(text2, "= x")), StringType); + AssertContainsExactly(pe[0].Analysis.GetTypesFromName("y", GetLineNumber(text1, "y =")), StringType); + }); + } + + [TestMethod] + public void TestCrossModuleFuncAndType() { + var text1 = @" +class Something(object): + def f(self): pass + def g(self): pass + + +def SomeFunc(): + x = Something() + return x +"; + var text2 = @" +from mod1 import SomeFunc + +x = SomeFunc() +"; + + var text3 = @" +from mod2 import x +a = x +"; + + PermutedTest("mod", new[] { text1, text2, text3 }, (pe) => { + AssertContainsExactly(pe[2].Analysis.GetMembersFromName("a", GetLineNumber(text3, "a = ")), + GetUnion(_objectMembers, "f", "g")); + }); + } + + [TestMethod] + public void TestMembersAfterError() { + var text = @" +class X(object): + def f(self): + return self. + + def g(self): + pass + + def h(self): + pass +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetMembersFromName("self", GetLineNumber(text, "self")), + GetUnion(_objectMembers, "f", "g", "h")); + } + + [TestMethod] + public void TestGenericIndexing() { + // indexing into a generic type should know how the type info + // flows through + var text = @" +from System.Collections.Generic import List +x = List[int]() +"; + var entry = ProcessText(text); + + // AreEqual(entry.GetMembersFromName('x', len(text) - 1), + // get_intersect_members_clr(List[int])) + var self = new List(entry.GetMembersFromName("x", GetLineNumber(text, "x ="))); + Assert.IsTrue(self.Contains("AddRange")); + } + + [TestMethod] + public void TestReturnTypesCollapsing() { + // indexing into a generic type should know how the type info + // flows through + var text = @" +from System import AppDomain +asm = AppDomain.CurrentDomain.DefineDynamicAssembly() +mod = asm.DefineDynamicModule() +mod. +"; + var entry = ProcessText(text); + var tooltips = entry.GetMembers("mod.", GetLineNumber(text, "mod =")) + .Where(m => m.Name == "CreateGlobalFunctions") + .Select(m => m.ToolTip) + .ToArray(); + Assert.AreEqual(1, tooltips.Length); + + var indexes = tooltips[0].FindIndexesOf("CreateGlobalFunctions").ToArray(); + Assert.AreEqual(1, indexes.Length); + } + + [TestMethod] + public void TestProperty() { + var text = @" +class x(object): + @property + def SomeProp(self): + return 42 + +a = x().SomeProp +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("a", GetLineNumber(text, "a =")), IntType); + } + + [TestMethod] + public void TestStaticMethod() { + var text = @" +class x(object): + @staticmethod + def StaticMethod(value): + return value + +a = x().StaticMethod(4.0) +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("a", GetLineNumber(text, "a = ")), FloatType); + } + + [TestMethod] + public void TestClassMethod() { + var text = @" +class x(object): + @classmethod + def ClassMethod(cls): + return cls + +a = x().ClassMethod() +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("a", GetLineNumber(text, "a =")), TypeType); + } + + [TestMethod] + public void TestAssignSelf() { + var text = @" +class x(object): + def __init__(self): + self.x = 'abc' + def f(self): + pass +"; + var entry = ProcessText(text); + AssertContains(entry.GetMembersFromName("self", GetLineNumber(text, "pass")), "x"); + AssertContainsExactly(entry.GetMembers("self.x.", GetLineNumber(text, "pass")).Select(m => m.Name), _strMembers); + } + + [TestMethod] + public void TestAssignEvent() { + var text = @" +import System + +def f(sender, args): + pass + +System.AppDomain.CurrentDomain.AssemblyLoad += f +"; + var entry = ProcessText(text); + Assert.IsTrue(entry.GetMembersFromName("args", GetLineNumber(text, "pass")).Any(s => s == "LoadedAssembly")); + } + + /* + [TestMethod] + public void TestOverrideParams() { + var text = @" +import System + +class MyArrayList(System.Collections.ArrayList): + def AddRange(self, col): + x = col +"; + var entry = ProcessText(text); + var x = entry.GetMembersFromName("x", text.IndexOf("x = col")).ToSet(); + AssertContainsExactly(x, GetMembers(ClrModule.GetPythonType(typeof(System.Collections.ICollection)), true)); + }*/ + + class EmptyAnalysisCookie : IAnalysisCookie { + public static EmptyAnalysisCookie Instance = new EmptyAnalysisCookie(); + public string GetLine(int lineNo) { + throw new NotImplementedException(); + } + } + + [TestMethod] + public void TestPackage() { + var src1 = GetSourceUnit("", @"C:\\Test\\Lib\\foo\\__init__.py"); + + var src2 = GetSourceUnit(@" +from foo.y import abc +import foo.y as y +", @"C:\\Test\\Lib\\foo\\x.py"); + + var src3 = GetSourceUnit(@" +abc = 42 +", @"C:\\Test\\Lib\\foo\\y.py"); + + var state = new ProjectState(_engine); + + var package = state.AddModule("foo", src1.Path, EmptyAnalysisCookie.Instance); + var x = state.AddModule("foo.x", src2.Path, EmptyAnalysisCookie.Instance); + var y = state.AddModule("foo.y", src3.Path, EmptyAnalysisCookie.Instance); + + Prepare(package, src1); + Prepare(x, src2); + Prepare(y, src3); + + package.Analyze(); + x.Analyze(); + y.Analyze(); + + Assert.AreEqual(x.Analysis.GetValues("y", 1).First().Description, "Python module foo.y"); + AssertContainsExactly(x.Analysis.GetTypesFromName("abc", 1), IntType); + } + + private static void Prepare(ProjectEntry entry, SourceUnit sourceUnit) { + CollectingErrorSink errorSink = new CollectingErrorSink(); + using (var parser = Utils.CreateParser(sourceUnit, errorSink)) { + entry.UpdateTree(parser.ParseFile(true), null); + } + } + + + /// + /// Verify that the analyzer has the proper algorithm for turning a filename into a package name + /// + [TestMethod] + public void TestPathToModuleName() { + string nzmathPath = Path.Combine(Environment.GetEnvironmentVariable("DLR_ROOT"), @"External.LCA_RESTRICTED\Languages\IronPython\Math"); + + Assert.AreEqual(PythonAnalyzer.PathToModuleName(Path.Combine(nzmathPath, @"nzmath\factor\__init__.py")), "nzmath.factor"); + Assert.AreEqual(PythonAnalyzer.PathToModuleName(Path.Combine(nzmathPath, @"nzmath\factor\find.py")), "nzmath.factor.find"); + } + + [TestMethod] + public void TestDefaults() { + var text = @" +def f(x = 42): + return x + +a = f() +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("a", GetLineNumber(text, "a =")), IntType); + } + + [TestMethod] + public void TestNegCallProperty() { + // invalid code, this shouldn't crash us. + var text = @" +import System +x = System.String.Length() +y = System.Environment.CurrentDirectory() +"; + ProcessText(text); + } + + [TestMethod] + public void TestClassInit() { + var text = @" +class X: + def __init__(self, value): + self.value = value + +a = X(2) +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("value", GetLineNumber(text, " = value")), IntType); + } + + [TestMethod] + public void TestClassNew() { + var text = @" +class X: + def __new__(cls, value): + res = object.__new__(cls) + res.value = value + return res + +a = X(2) +"; + var entry = ProcessText(text); + AssertContainsExactly(entry.GetTypesFromName("value", GetLineNumber(text, " = value")), IntType); + } + + private static IEnumerable GetVariableDescriptions(ModuleAnalysis entry, string variable, int position) { + return entry.GetValues(variable, position).Select(m => m.Description); + } + + [TestMethod] + public void TestQuickInfo() { + var text = @" +import System +import sys +from System.Collections import ArrayList +a = 41.0 +b = 42L +c = 'abc' +x = (2, 3, 4) +y = [2, 3, 4] +z = 43 + +class foo(object): + @property + def f(self): pass + + def g(self): pass + +d = foo() + +e = System.Collections.ArrayList() + +def f(): + print 'hello' + return 'abc' + +def g(): + return System + return c.Length +"; + var entry = ProcessText(text); + + AssertContainsExactly(GetVariableDescriptions(entry, "foo()", 1), "foo instance"); + AssertContainsExactly(GetVariableDescriptions(entry, "int()", 1), "int"); + AssertContainsExactly(GetVariableDescriptions(entry, "a", 1), "float"); + AssertContainsExactly(GetVariableDescriptions(entry, "a", 1), "float"); + AssertContainsExactly(GetVariableDescriptions(entry, "b", 1), "long"); + AssertContainsExactly(GetVariableDescriptions(entry, "c", 1), "str"); + AssertContainsExactly(entry.GetValues("x", 1).Select(v => v.Description.Substring(0, 5)), "tuple"); + AssertContainsExactly(entry.GetValues("y", 1).Select(v => v.Description.Substring(0, 4)), "list"); + AssertContainsExactly(GetVariableDescriptions(entry, "z", 1), "int"); + AssertContainsExactly(GetVariableDescriptions(entry, "min", 1), "built-in function min"); + AssertContainsExactly(GetVariableDescriptions(entry, "list.append", 1), "built-in method append"); + AssertContainsExactly(GetVariableDescriptions(entry, "System.String.Length", 1), "property of type "); + AssertContainsExactly(GetVariableDescriptions(entry, "\"abc\".Length", 1), "int"); + AssertContainsExactly(GetVariableDescriptions(entry, "c.Length", 1), "int"); + AssertContainsExactly(GetVariableDescriptions(entry, "System.Environment.CurrentDirectory", 1), "str"); + AssertContainsExactly(GetVariableDescriptions(entry, "d", 1), "foo instance"); + AssertContainsExactly(GetVariableDescriptions(entry, "e", 1), "ArrayList"); + AssertContainsExactly(GetVariableDescriptions(entry, "ArrayList", 1), "type ArrayList"); + AssertContainsExactly(GetVariableDescriptions(entry, "e.Count", 1), "int"); + AssertContainsExactly(GetVariableDescriptions(entry, "sys", 1), "built-in module sys"); + AssertContainsExactly(GetVariableDescriptions(entry, "f", 1), "def f(...)"); + AssertContainsExactly(GetVariableDescriptions(entry, "foo.f", 1), "def f(...)"); + AssertContainsExactly(GetVariableDescriptions(entry, "foo().g", 1), "method g of foo objects "); + AssertContainsExactly(GetVariableDescriptions(entry, "foo", 1), "class foo"); + AssertContainsExactly(GetVariableDescriptions(entry, "System.DBNull.Value", 1), "DBNull"); + AssertContainsExactly(GetVariableDescriptions(entry, "System.StringSplitOptions", 1), "type StringSplitOptions"); + //AssertContainsExactly(GetVariableDescriptions(entry, "System.StringSplitOptions.RemoveEmptyEntries", 0), "field of type StringSplitOptions"); + AssertContainsExactly(GetVariableDescriptions(entry, "g", 1), "def g(...)"); // return info could be better + AssertContainsExactly(GetVariableDescriptions(entry, "System.AppDomain.DomainUnload", 1), "event of type System.EventHandler"); + AssertContainsExactly(GetVariableDescriptions(entry, "None", 1), "None"); + + + } + + [TestMethod] + public void TestRecurisveDataStructures() { + var text = @" +d = {} +d[0] = d +"; + var entry = ProcessText(text); + + AssertContainsExactly(GetVariableDescriptions(entry, "d", 1), "dict({int : dict}"); + } + + /// + /// Variable is refered to in the base class, defined in the derived class, we should know the type information. + /// + [TestMethod] + public void TestBaseReferencedDerivedDefined() { + var text = @" +class Base(object): + def f(self): + x = self.map + +class Derived(Base): + def __init__(self): + self.map = {} + +pass +"; + + var entry = ProcessText(text); + var members = entry.GetMembers("Derived().", GetLineNumber(text, "pass")).ToArray(); + var map = members.First(x => x.Name == "map"); + + Assert.AreEqual(map.MemberType, ResultType.Field); + } + + #endregion + + #region Helpers + + static AnalysisTest() { + _engine = Python.CreateEngine(); + PyObjectType = ClrModule.GetPythonType(typeof(object)); + IntType = ClrModule.GetPythonType(typeof(int)); + ComplexType = ClrModule.GetPythonType(typeof(Complex)); + StringType = ClrModule.GetPythonType(typeof(string)); + FloatType = ClrModule.GetPythonType(typeof(double)); + TypeType = ClrModule.GetPythonType(typeof(PythonType)); + ListType = ClrModule.GetPythonType(typeof(List)); + TupleType = ClrModule.GetPythonType(typeof(PythonTuple)); + BoolType = ClrModule.GetPythonType(typeof(bool)); + FunctionType = ClrModule.GetPythonType(typeof(PythonFunction)); + GeneratorType = ClrModule.GetPythonType(typeof(PythonGenerator)); + + _objectMembers = GetMembers(PyObjectType, false); + _strMembers = GetMembers(StringType, false); + _listMembers = GetMembers(ListType, false); + _intMembers = GetMembers(IntType, false); + _functionMembers = GetMembers(FunctionType, false); + + _objectMembersClr = GetMembers(PyObjectType, true); + _strMembersClr = GetMembers(StringType, true); + + Assert.IsTrue(_objectMembers.Length < _objectMembersClr.Length); + Assert.IsTrue(_strMembers.Length < _strMembersClr.Length); + } + + private static string[] GetMembers(object obj, bool showClr) { + var dir = showClr ? ClrModule.DirClr(obj) : ClrModule.Dir(obj); + int len = dir.__len__(); + string[] result = new string[len]; + for (int i = 0; i < len; i++) { + Assert.IsTrue(dir[i] is string); + result[i] = dir[i] as string; + } + return result; + } + + private static SourceUnit GetSourceUnit(string text, string name) { + var textContent = new MyTextContentProvider(text); + var languageContext = HostingHelpers.GetLanguageContext(_engine); + return new SourceUnit(languageContext, textContent, name, SourceCodeKind.File); + } + + private static SourceUnit GetSourceUnit(string text) { + return GetSourceUnit(text, "foo"); + } + + private static ModuleAnalysis ProcessText(string text) { + var sourceUnit = GetSourceUnit(text, "foo"); + return ParseText(new ProjectState(_engine), sourceUnit, "foo").Analysis; + } + + /// + /// Returns all the permutations of the set [0 ... n-1] + /// + /// + /// + private static IEnumerable> Permutations(int n) { + if (n <= 0) { + yield return new List(); + } else { + foreach (var prev in Permutations(n - 1)) { + for (int i = n - 1; i >= 0; i--) { + var result = new List(prev); + result.Insert(i, n - 1); + yield return result; + } + } + } + } + + private IEnumerable MakeModulePermutations(string prefix, string[] code) { + foreach (var p in Permutations(code.Length)) { + var result = new ProjectEntry[code.Length]; + var state = new ProjectState(_engine); + for (int i = 0; i < code.Length; i++) { + result[p[i]] = state.AddModule(prefix + (p[i] + 1).ToString(), "foo", null); + } + for (int i = 0; i < code.Length; i++) { + Prepare(result[p[i]], GetSourceUnit(code[p[i]])); + } + for (int i = 0; i < code.Length; i++) { + result[p[i]].Analyze(); + } + yield return result; + } + } + + /// + /// For a given set of module definitions, build analysis info for each unique permutation + /// of the ordering of the defintions and run the test against each analysis. + /// + /// Prefix for the module names. The first source text will become prefix + "1", etc. + /// The source code for each of the modules + /// The test to run against the analysis + private void PermutedTest(string prefix, string[] code, Action test) { + foreach (var pe in MakeModulePermutations(prefix, code)) { + test(pe); + } + } + + private string MakeText(IEnumerable values) { + var sb = new StringBuilder("{"); + foreach (var value in values) { + if (sb.Length > 1) { + sb.Append(", "); + } + if (value is PythonType) { + sb.AppendFormat("Type({0})", PythonType.Get__name__((PythonType)(object)value)); + } else { + sb.Append(value.ToString()); + } + } + sb.Append("}"); + return sb.ToString(); + } + + private void AssertContains(IEnumerable source, T value) { + foreach (var v in source) { + if (v.Equals(value)) { + return; + } + } + + Assert.Fail(String.Format("{0} does not contain {1}", MakeText(source), value)); + } + + private void AssertDoesntContain(IEnumerable source, T value) { + foreach (var v in source) { + if (v.Equals(value)) { + Assert.Fail(String.Format("{0} does not contain {1}", MakeText(source), value)); + } + } + + } + + private void AssertContainsExactly(IEnumerable source, params T[] values) { + AssertContainsExactly(new HashSet(source), values); + } + + private void AssertContainsExactly(HashSet set, params T[] values) { + if (set.ContainsExactly(values)) { + return; + } + Assert.Fail(String.Format("Expected {0}, got {1}", MakeText(values), MakeText(set))); + } + + private static string[] GetUnion(params object[] objs) { + var result = new HashSet(); + foreach (var obj in objs) { + if (obj is string) { + result.Add((string)obj); + } else if (obj is IEnumerable) { + result.UnionWith((IEnumerable)obj); + } else { + throw new NotImplementedException("Non-string member"); + } + } + return result.ToArray(); + } + + private static string[] GetIntersection(IEnumerable first, params IEnumerable[] remaining) { + var result = new HashSet(first); + foreach (var obj in remaining) { + result.IntersectWith((IEnumerable)obj); + } + return result.ToArray(); + } + + #endregion + } +} diff --git a/Tools/IronStudio/AnalysisTest/AnalysisTest.csproj b/Tools/IronStudio/AnalysisTest/AnalysisTest.csproj new file mode 100644 index 0000000000..d7445fe122 --- /dev/null +++ b/Tools/IronStudio/AnalysisTest/AnalysisTest.csproj @@ -0,0 +1,98 @@ + + + + + Debug + AnyCPU + 2.0 + {A9180A68-2BD8-4F9B-9D25-C6532C124B52} + Exe + Properties + AnalysisTest + AnalysisTest + v4.0 + 512 + + SAK + SAK + SAK + SAK + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + true + full + false + $(SolutionDir)..\bin\Debug + DEBUG;TRACE;$(SignedSym) + prompt + 4 + true + + + pdbonly + true + $(SolutionDir)..\bin\Release + TRACE;$(SignedSym) + prompt + 4 + x86 + + + + + + + + + + + + + + + + + + + {155CE436-1669-4A48-8095-410F2430237F} + IronPython.Modules + + + {95289EA9-5778-489D-AB48-F81F2CE2DA32} + IronPython %28Languages\IronPython\IronPython%29 + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Dynamic + + + {ACDD9B9E-8FE6-439C-9521-1CCBA47F6143} + Microsoft.Scripting.Metadata + + + {02FF0909-F5AD-48CF-A86A-345E721B7E40} + Microsoft.Scripting + + + {B2E267A2-4836-40D7-817E-49D9D0E88435} + IronPythonToolsCore + + + {FA7BE5F5-E04F-4613-B7AC-70CE10D1BB68} + IronPythonTools + + + {843716AE-38B3-4723-963C-950DD06BC4B8} + IronStudioCore + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/AnalysisTest/AnalysisTest.csproj.user b/Tools/IronStudio/AnalysisTest/AnalysisTest.csproj.user new file mode 100644 index 0000000000..c4ad365bed --- /dev/null +++ b/Tools/IronStudio/AnalysisTest/AnalysisTest.csproj.user @@ -0,0 +1,11 @@ + + + + TestInstanceVariable + + + TestCrossModuleFuncAndType + Program + F:\Product\0\Dlr\Tools\bin\Debug\AnalysisTest.exe + + \ No newline at end of file diff --git a/Tools/IronStudio/AnalysisTest/AnalysisTest.csproj.vspscc b/Tools/IronStudio/AnalysisTest/AnalysisTest.csproj.vspscc new file mode 100644 index 0000000000..feffdecaa4 --- /dev/null +++ b/Tools/IronStudio/AnalysisTest/AnalysisTest.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/Tools/IronStudio/AnalysisTest/Assert.cs b/Tools/IronStudio/AnalysisTest/Assert.cs new file mode 100644 index 0000000000..52d4110325 --- /dev/null +++ b/Tools/IronStudio/AnalysisTest/Assert.cs @@ -0,0 +1,43 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AnalysisTest { + class Assert { + internal static void IsTrue(bool p) { + if (!p) { + Fail("not true"); + } + } + + internal static void AreEqual(object p, object p_2) { + if (p == null & p_2 == null) { + return; + } + if (p == null || + !p.Equals(p_2)) { + Fail(String.Format("{0} != {1}", p, p_2)); + } + } + + internal static void Fail(string p) { + throw new Exception(p); + } + } + +} diff --git a/Tools/IronStudio/AnalysisTest/ExtensionMethods.cs b/Tools/IronStudio/AnalysisTest/ExtensionMethods.cs new file mode 100644 index 0000000000..da97297979 --- /dev/null +++ b/Tools/IronStudio/AnalysisTest/ExtensionMethods.cs @@ -0,0 +1,52 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AnalysisTest { + public static class ExtensionMethods { + public static IEnumerable FindIndexesOf(this string s, string substring) { + int pos = 0; + while (true) { + pos = s.IndexOf(substring, pos); + if (pos < 0) { + break; + } + yield return pos; + pos++; + } + } + + public static HashSet ToSet(this IEnumerable enumeration) { + return new HashSet(enumeration); + } + + public static bool ContainsExactly(this HashSet set, IList values) { + if (set.Count != values.Count) { + return false; + } + foreach (var value in values) { + if (!set.Contains(value)) { + return false; + } + } + return true; + } + } + + +} diff --git a/Tools/IronStudio/AnalysisTest/MyTextContentProvider.cs b/Tools/IronStudio/AnalysisTest/MyTextContentProvider.cs new file mode 100644 index 0000000000..d4bf3de93c --- /dev/null +++ b/Tools/IronStudio/AnalysisTest/MyTextContentProvider.cs @@ -0,0 +1,34 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Scripting; +using System.IO; + +namespace AnalysisTest { + internal class MyTextContentProvider : TextContentProvider { + private readonly string _text; + + public MyTextContentProvider(string text) { + _text = text; + } + + public override SourceCodeReader GetReader() { + return new SourceCodeReader(new StringReader(_text), Encoding.Default); + } + } +} diff --git a/Tools/IronStudio/AnalysisTest/Properties/AssemblyInfo.cs b/Tools/IronStudio/AnalysisTest/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..ba98989857 --- /dev/null +++ b/Tools/IronStudio/AnalysisTest/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("AnalysisTest")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("MSIT")] +[assembly: AssemblyProduct("AnalysisTest")] +[assembly: AssemblyCopyright("Copyright © MSIT 2009")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("5e509824-e12e-42d7-84b8-eb88f44298d4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tools/IronStudio/AnalysisTest/TestMethod.cs b/Tools/IronStudio/AnalysisTest/TestMethod.cs new file mode 100644 index 0000000000..94ad2bb5d5 --- /dev/null +++ b/Tools/IronStudio/AnalysisTest/TestMethod.cs @@ -0,0 +1,23 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace AnalysisTest { + sealed class PerfMethodAttribute : Attribute { + } +} diff --git a/Tools/IronStudio/CodeCoverage.testsettings b/Tools/IronStudio/CodeCoverage.testsettings new file mode 100644 index 0000000000..936af5708d --- /dev/null +++ b/Tools/IronStudio/CodeCoverage.testsettings @@ -0,0 +1,49 @@ + + + These are test settings for code coverage runs + + + + + + + + + + + + + + + + + +
+
+
+
+ + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/Common.proj b/Tools/IronStudio/Common.proj new file mode 100644 index 0000000000..53b8004027 --- /dev/null +++ b/Tools/IronStudio/Common.proj @@ -0,0 +1,9 @@ + + + + + $(MSBuildThisFileDirectory)..\..\Solutions\ + $(VS100COMNTOOLS)\..\IDE + C:\Program Files (x86)\Microsoft Visual Studio 2010 SDK + + diff --git a/Tools/IronStudio/IpyMsi.bat b/Tools/IronStudio/IpyMsi.bat new file mode 100644 index 0000000000..752542f52f --- /dev/null +++ b/Tools/IronStudio/IpyMsi.bat @@ -0,0 +1,7 @@ +rem produces an MSI for IronPython w/ IronPython Tools +pushd %DLR_ROOT%\Scripts\Python +%DLR_ROOT%\Util\IronPython\ipy.exe gopackage\main.py --ironpython --version 2.7 --toolsbin --test_buildable --bail_on_failure --dlr_enlist %DLR_ROOT% + +xcopy /y %TEMP%\gopackage\Msi\IronPython.msi . + +popd \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/GlobalSuppressions.cs b/Tools/IronStudio/IronPythonTools/GlobalSuppressions.cs new file mode 100644 index 0000000000..85b00281af --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/GlobalSuppressions.cs @@ -0,0 +1,25 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. Project-level +// suppressions either have no target or are given a specific target +// and scoped to a namespace, type, member, etc. +// +// To add a suppression to this file, right-click the message in the +// Error List, point to "Suppress Message(s)", and click "In Project +// Suppression File". You do not need to add suppressions to this +// file manually. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")] diff --git a/Tools/IronStudio/IronPythonTools/Guids.cs b/Tools/IronStudio/IronPythonTools/Guids.cs new file mode 100644 index 0000000000..53e13cc600 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Guids.cs @@ -0,0 +1,28 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +// Guids.cs +// MUST match guids.h +using System; + +namespace Microsoft.IronPythonTools +{ + static class GuidList + { + public const string guidIronPythonToolsPkgString = "6dbd7c1e-1f1b-496d-ac7c-c55dae66c783"; + public const string guidIronPythonToolsCmdSetString = "bdfa79d2-2cd2-474a-a82a-ce8694116825"; + + public static readonly Guid guidIronPythonToolsCmdSet = new Guid(guidIronPythonToolsCmdSetString); + }; +} \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/Icons/OpenFolder.bmp b/Tools/IronStudio/IronPythonTools/Icons/OpenFolder.bmp new file mode 100644 index 0000000000..7ad88b5db2 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/OpenFolder.bmp differ diff --git a/Tools/IronStudio/IronPythonTools/Icons/PackageFolder.bmp b/Tools/IronStudio/IronPythonTools/Icons/PackageFolder.bmp new file mode 100644 index 0000000000..cf3038c1b9 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/PackageFolder.bmp differ diff --git a/Tools/IronStudio/IronPythonTools/Icons/PythonApplication.bmp b/Tools/IronStudio/IronPythonTools/Icons/PythonApplication.bmp new file mode 100644 index 0000000000..c72f600f00 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/PythonApplication.bmp differ diff --git a/Tools/IronStudio/IronPythonTools/Icons/PythonConsoleAppTemplateIcon.ico b/Tools/IronStudio/IronPythonTools/Icons/PythonConsoleAppTemplateIcon.ico new file mode 100644 index 0000000000..8c302f7492 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/PythonConsoleAppTemplateIcon.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Icons/PythonConsoleIcon_24Bit.bmp b/Tools/IronStudio/IronPythonTools/Icons/PythonConsoleIcon_24Bit.bmp new file mode 100644 index 0000000000..e9f100999a Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/PythonConsoleIcon_24Bit.bmp differ diff --git a/Tools/IronStudio/IronPythonTools/Icons/PythonConsoleIcon_32Bit.bmp b/Tools/IronStudio/IronPythonTools/Icons/PythonConsoleIcon_32Bit.bmp new file mode 100644 index 0000000000..880f7f0259 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/PythonConsoleIcon_32Bit.bmp differ diff --git a/Tools/IronStudio/IronPythonTools/Icons/PythonEmptyApp.ico b/Tools/IronStudio/IronPythonTools/Icons/PythonEmptyApp.ico new file mode 100644 index 0000000000..785ea5f291 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/PythonEmptyApp.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Icons/PythonEmptyWebsiteTemplateIcon.ico b/Tools/IronStudio/IronPythonTools/Icons/PythonEmptyWebsiteTemplateIcon.ico new file mode 100644 index 0000000000..f389a366cd Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/PythonEmptyWebsiteTemplateIcon.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Icons/PythonFile.bmp b/Tools/IronStudio/IronPythonTools/Icons/PythonFile.bmp new file mode 100644 index 0000000000..fdb5987db3 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/PythonFile.bmp differ diff --git a/Tools/IronStudio/IronPythonTools/Icons/PythonFileTemplateIcon.ico b/Tools/IronStudio/IronPythonTools/Icons/PythonFileTemplateIcon.ico new file mode 100644 index 0000000000..3cfc469560 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/PythonFileTemplateIcon.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Icons/PythonLibraryTemplateIcon.ico b/Tools/IronStudio/IronPythonTools/Icons/PythonLibraryTemplateIcon.ico new file mode 100644 index 0000000000..112f22dd2b Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/PythonLibraryTemplateIcon.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Icons/PythonSilverlightTemplateIcon.ico b/Tools/IronStudio/IronPythonTools/Icons/PythonSilverlightTemplateIcon.ico new file mode 100644 index 0000000000..21d93dd2d9 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/PythonSilverlightTemplateIcon.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Icons/PythonWPFApp.ico b/Tools/IronStudio/IronPythonTools/Icons/PythonWPFApp.ico new file mode 100644 index 0000000000..0e5a55020d Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/PythonWPFApp.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Icons/PythonWebsite.bmp b/Tools/IronStudio/IronPythonTools/Icons/PythonWebsite.bmp new file mode 100644 index 0000000000..3b2d8f3709 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/PythonWebsite.bmp differ diff --git a/Tools/IronStudio/IronPythonTools/Icons/PythonWebsiteTemplateIcon.ico b/Tools/IronStudio/IronPythonTools/Icons/PythonWebsiteTemplateIcon.ico new file mode 100644 index 0000000000..c88e3dce8e Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/PythonWebsiteTemplateIcon.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Icons/PythonWindowsApplication.ico b/Tools/IronStudio/IronPythonTools/Icons/PythonWindowsApplication.ico new file mode 100644 index 0000000000..f01a0f776a Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/PythonWindowsApplication.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Icons/SystemIcon.ico b/Tools/IronStudio/IronPythonTools/Icons/SystemIcon.ico new file mode 100644 index 0000000000..d682160a03 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Icons/SystemIcon.ico differ diff --git a/Tools/IronStudio/IronPythonTools/IronPython Tools for VS License.rtf b/Tools/IronStudio/IronPythonTools/IronPython Tools for VS License.rtf new file mode 100644 index 0000000000..5f462b6881 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPython Tools for VS License.rtf @@ -0,0 +1,598 @@ +{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff38\deff0\stshfdbch11\stshfloch0\stshfhich0\stshfbi0\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};} +{\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New{\*\falt Arial};}{\f3\fbidi \froman\fcharset2\fprq2{\*\panose 05050102010706020507}Symbol{\*\falt Bookshelf Symbol 3};} +{\f10\fbidi \fnil\fcharset2\fprq2{\*\panose 05000000000000000000}Wingdings{\*\falt Symbol};}{\f11\fbidi \fmodern\fcharset128\fprq1{\*\panose 02020609040205080304}MS Mincho{\*\falt ?l?r ??\'81\'66c};} +{\f11\fbidi \fmodern\fcharset128\fprq1{\*\panose 02020609040205080304}MS Mincho{\*\falt ?l?r ??\'81\'66c};}{\f38\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604030504040204}Tahoma{\*\falt ?l?r ??u!??I};} +{\f39\fbidi \fswiss\fcharset0\fprq2{\*\panose 00000000000000000000}Trebuchet MS{\*\falt Univers};}{\f40\fbidi \fmodern\fcharset128\fprq1{\*\panose 00000000000000000000}@MS Mincho{\*\falt @MS Gothic};} +{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};}{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};} +{\fhimajor\f31502\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria;}{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};} +{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};}{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};} +{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};} +{\f42\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};}{\f43\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};}{\f45\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};} +{\f46\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};}{\f47\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};}{\f48\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};} +{\f49\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};}{\f50\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}{\f62\fbidi \fmodern\fcharset238\fprq1 Courier New CE{\*\falt Arial};} +{\f63\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr{\*\falt Arial};}{\f65\fbidi \fmodern\fcharset161\fprq1 Courier New Greek{\*\falt Arial};}{\f66\fbidi \fmodern\fcharset162\fprq1 Courier New Tur{\*\falt Arial};} +{\f67\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew){\*\falt Arial};}{\f68\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic){\*\falt Arial};}{\f69\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic{\*\falt Arial};} +{\f70\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese){\*\falt Arial};}{\f154\fbidi \fmodern\fcharset0\fprq1 MS Mincho Western{\*\falt ?l?r ??\'81\'66c};}{\f152\fbidi \fmodern\fcharset238\fprq1 MS Mincho CE{\*\falt ?l?r ??\'81\'66c};} +{\f153\fbidi \fmodern\fcharset204\fprq1 MS Mincho Cyr{\*\falt ?l?r ??\'81\'66c};}{\f155\fbidi \fmodern\fcharset161\fprq1 MS Mincho Greek{\*\falt ?l?r ??\'81\'66c};}{\f156\fbidi \fmodern\fcharset162\fprq1 MS Mincho Tur{\*\falt ?l?r ??\'81\'66c};} +{\f159\fbidi \fmodern\fcharset186\fprq1 MS Mincho Baltic{\*\falt ?l?r ??\'81\'66c};}{\f154\fbidi \fmodern\fcharset0\fprq1 MS Mincho Western{\*\falt ?l?r ??\'81\'66c};}{\f152\fbidi \fmodern\fcharset238\fprq1 MS Mincho CE{\*\falt ?l?r ??\'81\'66c};} +{\f153\fbidi \fmodern\fcharset204\fprq1 MS Mincho Cyr{\*\falt ?l?r ??\'81\'66c};}{\f155\fbidi \fmodern\fcharset161\fprq1 MS Mincho Greek{\*\falt ?l?r ??\'81\'66c};}{\f156\fbidi \fmodern\fcharset162\fprq1 MS Mincho Tur{\*\falt ?l?r ??\'81\'66c};} +{\f159\fbidi \fmodern\fcharset186\fprq1 MS Mincho Baltic{\*\falt ?l?r ??\'81\'66c};}{\f422\fbidi \fswiss\fcharset238\fprq2 Tahoma CE{\*\falt ?l?r ??u!??I};}{\f423\fbidi \fswiss\fcharset204\fprq2 Tahoma Cyr{\*\falt ?l?r ??u!??I};} +{\f425\fbidi \fswiss\fcharset161\fprq2 Tahoma Greek{\*\falt ?l?r ??u!??I};}{\f426\fbidi \fswiss\fcharset162\fprq2 Tahoma Tur{\*\falt ?l?r ??u!??I};}{\f427\fbidi \fswiss\fcharset177\fprq2 Tahoma (Hebrew){\*\falt ?l?r ??u!??I};} +{\f428\fbidi \fswiss\fcharset178\fprq2 Tahoma (Arabic){\*\falt ?l?r ??u!??I};}{\f429\fbidi \fswiss\fcharset186\fprq2 Tahoma Baltic{\*\falt ?l?r ??u!??I};}{\f430\fbidi \fswiss\fcharset163\fprq2 Tahoma (Vietnamese){\*\falt ?l?r ??u!??I};} +{\f431\fbidi \fswiss\fcharset222\fprq2 Tahoma (Thai){\*\falt ?l?r ??u!??I};}{\f432\fbidi \fswiss\fcharset238\fprq2 Trebuchet MS CE{\*\falt Univers};}{\f433\fbidi \fswiss\fcharset204\fprq2 Trebuchet MS Cyr{\*\falt Univers};} +{\f435\fbidi \fswiss\fcharset161\fprq2 Trebuchet MS Greek{\*\falt Univers};}{\f436\fbidi \fswiss\fcharset162\fprq2 Trebuchet MS Tur{\*\falt Univers};}{\f439\fbidi \fswiss\fcharset186\fprq2 Trebuchet MS Baltic{\*\falt Univers};} +{\f444\fbidi \fmodern\fcharset0\fprq1 @MS Mincho Western{\*\falt @MS Gothic};}{\f442\fbidi \fmodern\fcharset238\fprq1 @MS Mincho CE{\*\falt @MS Gothic};}{\f443\fbidi \fmodern\fcharset204\fprq1 @MS Mincho Cyr{\*\falt @MS Gothic};} +{\f445\fbidi \fmodern\fcharset161\fprq1 @MS Mincho Greek{\*\falt @MS Gothic};}{\f446\fbidi \fmodern\fcharset162\fprq1 @MS Mincho Tur{\*\falt @MS Gothic};}{\f449\fbidi \fmodern\fcharset186\fprq1 @MS Mincho Baltic{\*\falt @MS Gothic};} +{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};} +{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};} +{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};} +{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};} +{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};} +{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};} +{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};} +{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}{\fhimajor\f31528\fbidi \froman\fcharset238\fprq2 Cambria CE;} +{\fhimajor\f31529\fbidi \froman\fcharset204\fprq2 Cambria Cyr;}{\fhimajor\f31531\fbidi \froman\fcharset161\fprq2 Cambria Greek;}{\fhimajor\f31532\fbidi \froman\fcharset162\fprq2 Cambria Tur;} +{\fhimajor\f31535\fbidi \froman\fcharset186\fprq2 Cambria Baltic;}{\fhimajor\f31536\fbidi \froman\fcharset163\fprq2 Cambria (Vietnamese);}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};} +{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};} +{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};} +{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};} +{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};} +{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};} +{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};} +{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};} +{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};} +{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};} +{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};} +{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};} +{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} +{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;} +{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};} +{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};} +{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};}{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};} +{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255; +\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0; +\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\*\defchp \fs22\dbch\af11 }{\*\defpap \ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{ +\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\snext0 \sautoupd \sqformat \spriority0 Normal;}{\s1\ql \fi-357\li357\ri0\sb120\sa120\widctlpar\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls17\outlinelevel0\adjustright\rin0\lin357\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 +\b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext1 \slink15 \sqformat heading 1;}{\s2\ql \fi-363\li720\ri0\sb120\sa120\widctlpar +\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl1\outlinelevel1\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext2 \slink16 \sqformat heading 2;}{\s3\ql \fi-357\li1077\ri0\sb120\sa120\widctlpar\tx1077\jclisttab\tx1440\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl2\outlinelevel2\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \af38\afs19\alang1025 +\ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext3 \slink17 \sqformat heading 3;}{\s4\ql \fi-358\li1435\ri0\sb120\sa120\widctlpar +\jclisttab\tx1437\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl3\outlinelevel3\adjustright\rin0\lin1435\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext4 \slink18 \sqformat heading 4;}{\s5\ql \fi-357\li1792\ri0\sb120\sa120\widctlpar\tx1792\jclisttab\tx2155\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl4\outlinelevel4\adjustright\rin0\lin1792\itap0 \rtlch\fcs1 \af38\afs19\alang1025 +\ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext5 \slink19 \sqformat heading 5;}{\s6\ql \fi-357\li2149\ri0\sb120\sa120\widctlpar +\jclisttab\tx2152\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl5\outlinelevel5\adjustright\rin0\lin2149\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext6 \slink20 \sqformat heading 6;}{\s7\ql \fi-357\li2506\ri0\sb120\sa120\widctlpar\jclisttab\tx2509\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl6\outlinelevel6\adjustright\rin0\lin2506\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext7 \slink21 \sqformat heading 7;}{\s8\ql \fi-357\li2863\ri0\sb120\sa120\widctlpar +\jclisttab\tx2866\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl7\outlinelevel7\adjustright\rin0\lin2863\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext8 \slink22 \sqformat heading 8;}{\s9\ql \fi-358\li3221\ri0\sb120\sa120\widctlpar\jclisttab\tx3223\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl8\outlinelevel8\adjustright\rin0\lin3221\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext9 \slink23 \sqformat heading 9;}{\*\cs10 \additive \ssemihidden Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f0\hich\af0\dbch\af11\cgrid\langnp1033\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}{\* +\cs15 \additive \rtlch\fcs1 \ab\af0\afs32 \ltrch\fcs0 \b\fs32\kerning32\loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink1 \slocked \spriority9 Heading 1 Char;}{\*\cs16 \additive \rtlch\fcs1 \ab\af38 \ltrch\fcs0 +\b\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\langnp1033\langfenp1033 \sbasedon10 \slink2 \slocked Heading 2 Char;}{\*\cs17 \additive \rtlch\fcs1 \ab\af0\afs26 \ltrch\fcs0 \b\fs26\loch\f31502\hich\af31502\dbch\af31501 +\sbasedon10 \slink3 \slocked \ssemihidden \spriority9 Heading 3 Char;}{\*\cs18 \additive \rtlch\fcs1 \ab\af0\afs28 \ltrch\fcs0 \b\fs28\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \slink4 \slocked \ssemihidden \spriority9 Heading 4 Char;}{\*\cs19 +\additive \rtlch\fcs1 \ab\ai\af0\afs26 \ltrch\fcs0 \b\i\fs26\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \slink5 \slocked \ssemihidden \spriority9 Heading 5 Char;}{\*\cs20 \additive \rtlch\fcs1 \ab\af0 \ltrch\fcs0 +\b\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \slink6 \slocked \ssemihidden \spriority9 Heading 6 Char;}{\*\cs21 \additive \rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs24\loch\f31506\hich\af31506\dbch\af31505 +\sbasedon10 \slink7 \slocked \ssemihidden \spriority9 Heading 7 Char;}{\*\cs22 \additive \rtlch\fcs1 \ai\af0\afs24 \ltrch\fcs0 \i\fs24\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \slink8 \slocked \ssemihidden \spriority9 Heading 8 Char;}{\*\cs23 +\additive \rtlch\fcs1 \af0 \ltrch\fcs0 \loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink9 \slocked \ssemihidden \spriority9 Heading 9 Char;}{\s24\ql \li357\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin357\itap0 +\rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext24 Body 1;}{ +\s25\ql \li720\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext25 Body 2;}{\s26\ql \li1077\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext26 Body 3;}{\s27\ql \li1435\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1435\itap0 \rtlch\fcs1 +\af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext27 Body 4;}{ +\s28\ql \li1803\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1803\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext28 Body 5;}{\s29\ql \li2160\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin2160\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext29 Body 6;}{\s30\ql \li2506\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin2506\itap0 \rtlch\fcs1 +\af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext30 Body 7;}{ +\s31\ql \li2863\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin2863\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext31 Body 8;}{\s32\ql \li3221\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin3221\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext32 Body 9;}{\s33\ql \fi-357\li357\ri0\sb120\sa120\widctlpar\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls1\adjustright\rin0\lin357\itap0 +\rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext33 Bullet 1;}{\s34\ql \fi-363\li720\ri0\sb120\sa120\widctlpar +\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext34 Bullet 2;}{ +\s35\ql \fi-357\li1077\ri0\sb120\sa120\widctlpar\jclisttab\tx1080\wrapdefault\aspalpha\aspnum\faauto\ls3\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext35 Bullet 3;}{\s36\ql \fi-358\li1435\ri0\sb120\sa120\widctlpar\jclisttab\tx1437\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin1435\itap0 +\rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext36 Bullet 4;}{\s37\ql \fi-357\li1792\ri0\sb120\sa120\widctlpar +\jclisttab\tx1795\wrapdefault\aspalpha\aspnum\faauto\ls5\adjustright\rin0\lin1792\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext37 Bullet 5;}{ +\s38\ql \fi-357\li2149\ri0\sb120\sa120\widctlpar\jclisttab\tx2152\wrapdefault\aspalpha\aspnum\faauto\ls6\adjustright\rin0\lin2149\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext38 Bullet 6;}{\s39\ql \fi-357\li2506\ri0\sb120\sa120\widctlpar\jclisttab\tx2509\wrapdefault\aspalpha\aspnum\faauto\ls7\adjustright\rin0\lin2506\itap0 +\rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext39 Bullet 7;}{\s40\ql \fi-357\li2863\ri0\sb120\sa120\widctlpar +\jclisttab\tx2866\wrapdefault\aspalpha\aspnum\faauto\ls8\adjustright\rin0\lin2863\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext40 Bullet 8;}{ +\s41\ql \fi-358\li3221\ri0\sb120\sa120\widctlpar\jclisttab\tx3223\wrapdefault\aspalpha\aspnum\faauto\ls9\adjustright\rin0\lin3221\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon32 \snext41 Bullet 9;}{\s42\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\ab\af38\afs28\alang1025 \ltrch\fcs0 \b\fs28\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 Heading EULA;}{\s43\ql \li0\ri0\sb120\sa120\widctlpar\brdrb\brdrs\brdrw10\brsp20 +\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs28\alang1025 \ltrch\fcs0 \b\fs28\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 Heading Software Title;}{ +\s44\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext44 Preamble;}{\s45\ql \li0\ri0\sb120\sa120\widctlpar\brdrb\brdrs\brdrw10\brsp20 \wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 +\b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext1 Preamble Border;}{\s46\qc \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext46 Heading Warranty;}{\s47\ql \fi-360\li360\ri0\sb120\sa120\widctlpar +\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls11\outlinelevel0\adjustright\rin0\lin360\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 +Heading 1 Warranty;}{\s48\ql \fi-360\li720\ri0\sb120\sa120\widctlpar\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls11\ilvl1\outlinelevel1\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 Heading 2 Warranty;}{\s49\ql \fi-357\li1077\ri0\sb120\sa120\widctlpar +\tx1077\jclisttab\tx1440\wrapdefault\aspalpha\aspnum\faauto\ls10\ilvl2\outlinelevel2\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon3 \snext49 Heading 3 Bold;}{\s50\ql \fi-358\li1435\ri0\sb120\sa120\widctlpar\jclisttab\tx1437\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin1435\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\ul\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon36 \snext50 Bullet 4 Underline;}{\s51\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\af38\afs19\alang1025 \ltrch\fcs0 \fs19\ul\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon35 \snext51 Bullet 3 Underline;}{ +\s52\ql \li720\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\ul\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon25 \snext52 Body 2 Underline;}{\s53\ql \li1077\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\ul\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon26 \snext53 Body 3 Underline;}{\s54\ql \li0\ri0\sb120\sa120\sl480\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 +\rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext54 \slink55 Body Text Indent;}{\*\cs55 \additive \rtlch\fcs1 \af38\afs19 \ltrch\fcs0 \f38\fs19 +\sbasedon10 \slink54 \slocked \ssemihidden Body Text Indent Char;}{\s56\ql \fi-358\li1435\ri0\sb120\sa120\widctlpar\jclisttab\tx1437\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin1435\itap0 \rtlch\fcs1 \ai\af38\afs19\alang1025 \ltrch\fcs0 +\i\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon36 \snext56 Bullet 4 Italics;}{\*\cs57 \additive \rtlch\fcs1 \af38 \ltrch\fcs0 \f38\lang1033\langfe1033\langnp1033\langfenp1033 \sbasedon10 Body 2 Char;}{\* +\cs58 \additive \rtlch\fcs1 \af38 \ltrch\fcs0 \f38\lang1033\langfe1033\langnp1033\langfenp1033 \sbasedon10 Body 3 Char;}{\*\cs59 \additive \rtlch\fcs1 \af38 \ltrch\fcs0 \f38\lang1033\langfe1033\langnp1033\langfenp1033 \sbasedon10 Body 4 Char;}{\*\cs60 +\additive \rtlch\fcs1 \af38 \ltrch\fcs0 \f38\lang1033\langfe1033\langnp1033\langfenp1033 \sbasedon10 Body 1 Char;}{\s61\ql \li0\ri0\sb120\sa120\widctlpar\brdrt\brdrs\brdrw10\brsp20 \wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 +\rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon44 \snext61 Preamble Border Above;}{ +\s62\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext62 \slink63 \ssemihidden footnote text;}{\*\cs63 \additive \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \f38\fs20 \sbasedon10 \slink62 \slocked \ssemihidden Footnote Text Char;}{\*\cs64 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \super +\sbasedon10 \ssemihidden footnote reference;}{\s65\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext65 \slink66 \ssemihidden endnote text;}{\*\cs66 \additive \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \f38\fs20 \sbasedon10 \slink65 \slocked \ssemihidden +Endnote Text Char;}{\*\cs67 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \super \sbasedon10 \ssemihidden endnote reference;}{\s68\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 +\ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext68 \slink69 \ssemihidden annotation text;}{\*\cs69 \additive \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \f38\fs20 +\sbasedon10 \slink68 \slocked \ssemihidden Comment Text Char;}{\*\cs70 \additive \rtlch\fcs1 \af0\afs16 \ltrch\fcs0 \fs16 \sbasedon10 \ssemihidden annotation reference;}{\s71\ql \li0\ri0\sa160\sl-240\slmult0 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext71 Char;}{ +\s72\ql \li0\ri0\sa160\sl-240\slmult0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext72 Char Char Char Char;}{\*\cs73 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf2 \sbasedon10 Hyperlink;}{\s74\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\af38\afs16\alang1025 \ltrch\fcs0 \fs16\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext74 \slink75 \ssemihidden Balloon Text;}{\*\cs75 \additive \rtlch\fcs1 \af38\afs16 \ltrch\fcs0 \f38\fs16 +\sbasedon10 \slink74 \slocked \ssemihidden Balloon Text Char;}{\*\cs76 \additive \rtlch\fcs1 \ab\af39 \ltrch\fcs0 \b\f39\lang1033\langfe1033\langnp1033\langfenp1033 \sbasedon10 Heading 2 Char1;}{\*\cs77 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \sbasedon10 +page number;}{\s78\ql \li0\ri0\sa160\sl-240\slmult0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext78 Char Char Char Char1;}{\s79\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 +\ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \snext0 \styrsid8999754 Body 0 Bold;}{\s80\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \snext0 \styrsid8999754 Body 0;}{\s81\ql \li0\ri0\sb120\sa120\widctlpar +\tqc\tx4320\tqr\tx8640\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext81 \slink82 \styrsid11496811 header;}{\*\cs82 \additive \rtlch\fcs1 \af38\afs19 \ltrch\fcs0 \f38\fs19 \sbasedon10 \slink81 \slocked \ssemihidden Header Char;}{\s83\ql \li0\ri0\sb120\sa120\widctlpar +\tqc\tx4320\tqr\tx8640\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext83 \slink84 \styrsid11496811 footer;}{\*\cs84 \additive \rtlch\fcs1 \af38\afs19 \ltrch\fcs0 \f38\fs19 \sbasedon10 \slink83 \slocked \ssemihidden Footer Char;}{ +\s85\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs20\alang1025 \ltrch\fcs0 \b\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon68 \snext68 \slink86 \ssemihidden \sunhideused \styrsid8850911 annotation subject;}{\*\cs86 \additive \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\f38\fs20 \sbasedon69 \slink85 \slocked \ssemihidden \styrsid8850911 Comment Subject Char;}} +{\*\listtable{\list\listtemplateid176468498\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid692200086\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s41\fi-358\li3221 +\jclisttab\tx3223\lin3221 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23 +\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691 +\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;} +\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid196815738}{\list\listtemplateid186961718{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers +\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2160 +\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2880\jclisttab\tx2880\lin2880 } +{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc4 +\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc4\levelnfcn4\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative +\levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid259719883}{\list\listtemplateid-1793664660{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af0 \ltrch\fcs0 \b\i0\fbias0 \s47\fi-360\li360\jclisttab\tx360\lin360 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af0 \ltrch\fcs0 \b\i0\fbias0 \s48\fi-360\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'02\'02);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li1080\jclisttab\tx1080\lin1080 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'03(\'03);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'03(\'04);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li1800\jclisttab\tx1800\lin1800 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'05);}{\levelnumbers +\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \fbias0 \fi-360\li2520\jclisttab\tx2520\lin2520 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 +\fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li3240 +\jclisttab\tx3240\lin3240 }{\listname ;}\listid394402059}{\list\listtemplateid1928476992{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0 \s49\fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers +\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'02i.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'02A.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid398796681}{\list\listtemplateid789093748\listhybrid{\listlevel\levelnfc23\levelnfcn23 +\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid-317712510\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s34\fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691 +\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;} +\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040 +\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid477573462} +{\list\listtemplateid1948578256{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-357\li357 +\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af0\afs20 \ltrch\fcs0 \b\i0\fs20\fbias0 \fi-360\li720 +\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li1077 +\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 +\fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 +\b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid630479929}{\list\listtemplateid67698717{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'02\'00);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li360\jclisttab\tx360\lin360 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'02\'01);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02);}{\levelnumbers\'01;}\rtlch\fcs1 +\af0 \ltrch\fcs0 \fi-360\li1080\jclisttab\tx1080\lin1080 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'03);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li1440 +\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'04);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li1800\jclisttab\tx1800\lin1800 }{\listlevel +\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'05);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2520\jclisttab\tx2520\lin2520 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li3240\jclisttab\tx3240\lin3240 }{\listname ;}\listid700712945}{\list\listtemplateid-53848358{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0 \s1\fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0 \s2\fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0 \s3\fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \s4\fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \s5\fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \s6\fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4 +\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \s7\fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255 +\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \s8\fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255 +\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \s9\fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname +;}\listid752163927}{\list\listtemplateid2088029282{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 +\b\i0\f39\fs20\fbias0 \fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 +\b\i0\f39\fs20\fbias0 \fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 +\b\i0\f38\fs20\fbias0 \fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 +\b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers +\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers +\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers +;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;} +\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid800729109}{\list\listtemplateid-296591990\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s40\fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160 +\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760 +\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname +;}\listid810947713}{\list\listtemplateid1567531878{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 +\b\i0\f39\fs20\fbias0 \fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 +\b\i0\f39\fs20\fbias0 \fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 +\b0\i0\f39\fs20\fbias0 \fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 +\b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers +\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers +\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers +;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;} +\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid826823576}{\list\listtemplateid2088029282{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0 \fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid974869818} +{\list\listtemplateid-1813845996\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s39\fi-357\li2506\jclisttab\tx2509\lin2506 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 +\fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1219436735}{\list\listtemplateid-41362566\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s36\fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;} +\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 +\fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1559511898}{\list\listtemplateid-743794326\listhybrid +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid2033377338\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s35\fi-357\li1077\jclisttab\tx1080\lin1077 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;} +\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760 +\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 } +{\listname ;}\listid1567649130}{\list\listtemplateid1363474438\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid-1175557160\'01\u-3913 ?;}{\levelnumbers;} +\f3\fbias0 \s37\fi-357\li1792\jclisttab\tx1795\lin1792 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440 +\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace1077\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0 +{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1848404271}{\list\listtemplateid-1802592190\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\leveltemplateid1229593488\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s38\fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;} +\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600 +\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1877695764}{\list\listtemplateid1186249844\listhybrid{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid1637229796\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s33\fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc23\levelnfcn23 +\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;} +\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760 +\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 } +{\listname ;}\listid2054619191}}{\*\listoverridetable{\listoverride\listid2054619191\listoverridecount0\ls1}{\listoverride\listid477573462\listoverridecount0\ls2}{\listoverride\listid1567649130\listoverridecount0\ls3}{\listoverride\listid1559511898 +\listoverridecount0\ls4}{\listoverride\listid1848404271\listoverridecount0\ls5}{\listoverride\listid1877695764\listoverridecount0\ls6}{\listoverride\listid1219436735\listoverridecount0\ls7}{\listoverride\listid810947713\listoverridecount0\ls8} +{\listoverride\listid196815738\listoverridecount0\ls9}{\listoverride\listid398796681\listoverridecount0\ls10}{\listoverride\listid394402059\listoverridecount0\ls11}{\listoverride\listid700712945\listoverridecount0\ls12}{\listoverride\listid826823576 +\listoverridecount0\ls13}{\listoverride\listid630479929\listoverridecount0\ls14}{\listoverride\listid800729109\listoverridecount0\ls15}{\listoverride\listid974869818\listoverridecount0\ls16}{\listoverride\listid752163927\listoverridecount0\ls17} +{\listoverride\listid398796681\listoverridecount0\ls18}{\listoverride\listid398796681\listoverridecount0\ls19}{\listoverride\listid477573462\listoverridecount0\ls20}{\listoverride\listid259719883\listoverridecount0\ls21}}{\*\rsidtbl \rsid17701\rsid72953 +\rsid200783\rsid222748\rsid345491\rsid480810\rsid535495\rsid538148\rsid555183\rsid676065\rsid745150\rsid787759\rsid986785\rsid1006099\rsid1009112\rsid1012355\rsid1070219\rsid1122066\rsid1182701\rsid1206375\rsid1245853\rsid1342505\rsid1528414\rsid1529837 +\rsid1591306\rsid1722062\rsid1790012\rsid1800865\rsid1845488\rsid1901753\rsid1987218\rsid2173756\rsid2239916\rsid2571685\rsid2584538\rsid2765700\rsid2775782\rsid2781980\rsid2784514\rsid2818569\rsid2830425\rsid2962852\rsid3042060\rsid3162620\rsid3163049 +\rsid3370445\rsid3411320\rsid3411753\rsid3416253\rsid3439038\rsid3475551\rsid3611186\rsid3689565\rsid3739474\rsid3806252\rsid3822783\rsid4022155\rsid4023230\rsid4144829\rsid4202022\rsid4259872\rsid4287357\rsid4287841\rsid4457576\rsid4595328\rsid4738534 +\rsid4739523\rsid4793230\rsid4814690\rsid4865423\rsid4878548\rsid4995346\rsid5010248\rsid5062678\rsid5140435\rsid5185544\rsid5250241\rsid5405299\rsid5450553\rsid5459775\rsid5519492\rsid5525537\rsid5660926\rsid5718733\rsid5718961\rsid5773282\rsid5788093 +\rsid5901771\rsid6033147\rsid6042923\rsid6049329\rsid6119652\rsid6184270\rsid6227403\rsid6231754\rsid6304161\rsid6365404\rsid6373957\rsid6425843\rsid6453852\rsid6492030\rsid6498245\rsid6500924\rsid6506467\rsid6647886\rsid6758513\rsid6888647\rsid6889714 +\rsid6954571\rsid6971210\rsid7028642\rsid7100767\rsid7226971\rsid7282236\rsid7290457\rsid7345747\rsid7428746\rsid7433926\rsid7438204\rsid7495929\rsid7554964\rsid7619174\rsid7692510\rsid7754893\rsid7800249\rsid7878867\rsid8004214\rsid8132403\rsid8197303 +\rsid8214982\rsid8259998\rsid8324055\rsid8325040\rsid8419363\rsid8458805\rsid8545132\rsid8671477\rsid8679719\rsid8738620\rsid8745808\rsid8812012\rsid8850911\rsid8857738\rsid8858237\rsid8999754\rsid9071447\rsid9203972\rsid9261549\rsid9307880\rsid9321702 +\rsid9526807\rsid9649378\rsid9651656\rsid9664357\rsid9703728\rsid9778027\rsid9785620\rsid9796909\rsid9857610\rsid9860938\rsid9861873\rsid9964378\rsid10171790\rsid10179250\rsid10294454\rsid10749433\rsid10813938\rsid10882308\rsid10955317\rsid11084414 +\rsid11142543\rsid11347136\rsid11432977\rsid11496811\rsid11498068\rsid11622420\rsid11672016\rsid11686297\rsid11695497\rsid11754382\rsid11874088\rsid11882246\rsid11937164\rsid12000701\rsid12015935\rsid12020896\rsid12065226\rsid12199483\rsid12222130 +\rsid12255436\rsid12407360\rsid12585274\rsid12596065\rsid12664082\rsid12722678\rsid12797652\rsid12798176\rsid12913505\rsid13136677\rsid13309404\rsid13334496\rsid13388123\rsid13828315\rsid13832939\rsid13896616\rsid13908819\rsid13965668\rsid14168694 +\rsid14223456\rsid14293847\rsid14297853\rsid14380549\rsid14382435\rsid14443673\rsid14491415\rsid14705568\rsid14771509\rsid14825379\rsid14830971\rsid14889524\rsid14894057\rsid14897950\rsid14943232\rsid15007790\rsid15427736\rsid15493712\rsid15495555 +\rsid15545976\rsid15601712\rsid15811431\rsid15822672\rsid15872081\rsid15925451\rsid16141742\rsid16202142\rsid16385696\rsid16395859\rsid16406536\rsid16450365\rsid16542934\rsid16661796\rsid16712132\rsid16716683}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0 +\msmallFrac0\mdispDef1\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\title MICROSOFT [SoftwareType IS "Beta Software"=PRE-RELEASE ][SoftwareType IS "Evaluation Software"=EVALUATION ]SOFTWARE LICENSE TERMS} +{\creatim\yr2010\mo2\dy4\hr11\min52}{\revtim\yr2010\mo7\dy15\hr15\min11}{\version1}{\edmins0}{\nofpages4}{\nofwords1392}{\nofchars7935}{\nofcharsws9309}{\vern49247}}{\*\userprops {\propname db_build_version}\proptype30{\staticval 2.6}{\propname db_charger +_document_reference}\proptype3{\staticval 25146}{\propname db_charger_client_name}\proptype30{\staticval tbc}{\propname db_charger_matter_number}\proptype30{\staticval tbc}{\propname autosave}\proptype30{\staticval false}{\propname owner}\proptype30 +{\staticval REDMOND\'5ckathan}{\propname db_master_reference}\proptype30{\staticval USETERMS_MAINB}{\propname db_master_version}\proptype30{\staticval 20081001}{\propname db_master_clock}\proptype3{\staticval 783}{\propname db_master_name}\proptype30 +{\staticval Retail/OEM Software License Terms - Main}{\propname db_master_description}\proptype30{\staticval }{\propname db_output_filter_reference}\proptype30{\staticval }{\propname db_base_url}\proptype30{\staticval http://usetermassembly/dealbuilder_l +ive/DealBuilderNET/dealbuilder.aspx}{\propname ProductVersion}\proptype30{\staticval 0}{\propname MScom}\proptype11{\staticval 0}{\propname LanguageAll}\proptype30{\staticval English}{\propname CanadaAvail}\proptype11{\staticval 1}{\propname CanadaFrench} +\proptype11{\staticval 0}{\propname FileFormat}\proptype11{\staticval 0}{\propname SoftwareType}\proptype30{\staticval Beta Software}{\propname ProductName}\proptype30{\staticval Incubation Software}{\propname NumberOfCopies}\proptype30{\staticval Any num +ber of copies}{\propname MandatoryActivation}\proptype11{\staticval 0}{\propname BetaUseRight}\proptype30{\staticval On the user's premises}{\propname ProductKey}\proptype11{\staticval 0}{\propname ConfidentialInformation}\proptype11{\staticval 0} +{\propname Feedback}\proptype30{\staticval Optional}{\propname NetFramework}\proptype11{\staticval 0}{\propname InternetBasedServices}\proptype11{\staticval 0}{\propname InternetBasedServicesFeaturesDescOther}\proptype30{\staticval }{\propname TimeBomb} +\proptype11{\staticval 0}{\propname TermWhen}\proptype30{\staticval A certain number of years}{\propname TermYears}\proptype30{\staticval Two}{\propname TermCommRel}\proptype11{\staticval 0}{\propname db_commit}\proptype30{\staticval ProductVersion}} +{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect +\widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1\noxlattoyen +\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\hyphcaps0\formshade\horzdoc\dgmargin\dghspace95\dgvspace180\dghorigin1440\dgvorigin1440\dghshow2\dgvshow1 +\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\rempersonalinfo\rsidroot10813938 \fet0{\*\wgrffmtfilter 013f}\ilfomacatclnup0 +{\*\docvar {db_xml}{\'0d\'0dhttp://usetermassembly/dealbuilder_live/DealBuilderNET/dealbuilder.aspxmicrosoftmicrosoftmicrosoft25145tbctbcUSETERMS_MAINB2.6Retail/OEM Software License Terms - Main 20081001783trueuniquetrue +truetruetruetruetruetruelazyday_month_year,.day_month_year,._blank +rtffalsedraftingindefinitetrueautosave|text|falseowner|text|REDMOND +\'5ckathantruefalsetruepromptvaluepagegroup< +Value>sureunsureunknowndeferredfalsealiasfalseascendingfalsetruefalseRepeat< +Control NAME="db_input_heading_highlight_column" TYPE="string">CheckPrompt +AnswerDeferralGuidanceInsert your comments belowVariable/dealbuilder_live/help/dealb +uilder/help.htmlonsubmittruetruefalsetruefalsefalsetrue2dropdownsureUnknownfirstOtherlast20204Specify others:Specify other:11, and and/or or YesNo< +/Control>(%1 of %2)&\'3bnbsp\'3bvisibledigitsPrevNext&\'3bnbsp\'3b|&\'3bnbsp\'3b*aftera +ftertruefalseclient_side<\'3bU>\'3bWARNING:<\'3b/U>\'3b That page is no longer relevant because of answers given on this page or a previous page!enabledrelevant_pages0English100Beta SoftwareIncubation SoftwareAny number of copies0On the user&apos\'3bs premises00Optional000A certain number of yearsTwo0}}{\*\ftnsep \ltrpar \pard\plain \ltrpar +\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 +\ltrch\fcs0 \insrsid6049329 \chftnsep +\par }}{\*\ftnsepc \ltrpar \pard\plain \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid6049329 \chftnsepc +\par }}{\*\aftnsep \ltrpar \pard\plain \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid6049329 \chftnsep +\par }}{\*\aftnsepc \ltrpar \pard\plain \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid6049329 \chftnsepc +\par }}\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4 +\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (} +{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar +\qc \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid9785620 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 Apache License\line Version 2.0, January 2004\line } +{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 HYPERLINK "http://www.apache.org/licenses/" }{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 +\f0\fs24\insrsid9785620\charrsid12260020 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5800000068007400740070003a002f002f007700770077002e006100700061006300680065002e006f00720067002f006c006900630065006e007300650073002f000000795881f43b1d7f48af2c825dc48527630000 +0000a5ab0000}}}{\fldrslt {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\ul\cf2\insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 http://www.apache.org/licenses/}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 +\f0\fs24\insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 +\par }\pard \ltrpar\ql \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid9785620 {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +\par }{\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid9785620\charrsid12260020 {\*\bkmkstart definitions}\hich\af0\dbch\af11\loch\f0 1. Definitions}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid9785620\charrsid12260020 {\*\bkmkend definitions} +. +\par \hich\af0\dbch\af11\loch\f0 "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. +\par \hich\af0\dbch\af11\loch\f0 "Licensor" shall\hich\af0\dbch\af11\loch\f0 mean the copyright owner or entity authorized by the copyright owner that is granting the License. +\par \hich\af0\dbch\af11\loch\f0 "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that enti\hich\af0\dbch\af11\loch\f0 +ty. For the purposes of this definition, "control" means (i) the power, direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, +\hich\af0\dbch\af11\loch\f0 o\hich\af0\dbch\af11\loch\f0 r (iii) beneficial ownership of such entity. +\par \hich\af0\dbch\af11\loch\f0 "You" (or "Your") shall mean an individual or Legal Entity exercising permissions granted by this License. +\par \hich\af0\dbch\af11\loch\f0 "Source" form shall mean the preferred form for making modifications, including but not limited to sof\hich\af0\dbch\af11\loch\f0 tware source code, documentation source, and configuration files. +\par \hich\af0\dbch\af11\loch\f0 "Object" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and c\hich\af0\dbch\af11\loch\f0 +onversions to other media types. +\par \hich\af0\dbch\af11\loch\f0 "Work" shall mean the work of authorship, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Ap +\hich\af0\dbch\af11\loch\f0 pendix below). +\par \hich\af0\dbch\af11\loch\f0 +"Derivative Works" shall mean any work, whether in Source or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an origina +\hich\af0\dbch\af11\loch\f0 l work of authorship. For the purposes of this License, Derivative Works shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. +\par \hich\af0\dbch\af11\loch\f0 "Contribution" shall mean any work o\hich\af0\dbch\af11\loch\f0 +f authorship, including the original version of the Work and any modifications or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal +\hich\af0\dbch\af11\loch\f0 \hich\af0\dbch\af11\loch\f0 +Entity authorized to submit on behalf of the copyright owner. For the purposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communi +\hich\af0\dbch\af11\loch\f0 c\hich\af0\dbch\af11\loch\f0 +ation on electronic mailing lists, source code control systems, and issue tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or +\hich\af0\dbch\af11\loch\f0 \hich\af0\dbch\af11\loch\f0 otherwise designated in writing by the copyright owner as "Not a Contribution." +\par \hich\af0\dbch\af11\loch\f0 "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Wo\hich\af0\dbch\af11\loch\f0 rk. +\par }{\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid9785620\charrsid12260020 {\*\bkmkstart copyright}\hich\af0\dbch\af11\loch\f0 2. Grant of Copyright License}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid9785620\charrsid12260020 +{\*\bkmkend copyright}\hich\af0\dbch\af11\loch\f0 . Subject to the terms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, pre +\hich\af0\dbch\af11\loch\f0 pare Derivative Works of, publicly display, publicly perform, sublicense, and distribute the Work and such Derivative Works in Source or Object form. +\par }{\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid9785620\charrsid12260020 {\*\bkmkstart patent}\hich\af0\dbch\af11\loch\f0 3. Grant of Patent License}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid9785620\charrsid12260020 +{\*\bkmkend patent}\hich\af0\dbch\af11\loch\f0 . Subject to the terms and conditions of this License, each Contrib\hich\af0\dbch\af11\loch\f0 +utor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such licen +\hich\af0\dbch\af11\loch\f0 s\hich\af0\dbch\af11\loch\f0 +e applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute paten +\hich\af0\dbch\af11\loch\f0 t\hich\af0\dbch\af11\loch\f0 + litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You und +\hich\af0\dbch\af11\loch\f0 e\hich\af0\dbch\af11\loch\f0 r this License for that Work shall terminate as of the date such litigation is filed. +\par }{\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid9785620\charrsid12260020 {\*\bkmkstart redistribution}\hich\af0\dbch\af11\loch\f0 4. Redistribution}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid9785620\charrsid12260020 +{\*\bkmkend redistribution}\hich\af0\dbch\af11\loch\f0 . You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modificatio\hich\af0\dbch\af11\loch\f0 +ns, and in Source or Object form, provided that You meet the following conditions: +\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 a.\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\sb100\sa240\sbauto1\widctlpar +\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls21\adjustright\rin0\lin720\itap0\pararsid9785620 {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 +You must give any other recipients of the Work or Derivative Works a copy of this License; and +\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 b.\tab}\hich\af0\dbch\af11\loch\f0 You must cause any modified files to carry prominent notices stating t\hich\af0\dbch\af11\loch\f0 +hat You changed the files; and +\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 c.\tab}\hich\af0\dbch\af11\loch\f0 +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to a\hich\af0\dbch\af11\loch\f0 +ny part of the Derivative Works; and +\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 d.\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\sb100\sa100\sbauto1\saauto1\widctlpar +\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls21\adjustright\rin0\lin720\itap0\pararsid9785620 {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excludi\hich\af0\dbch\af11\loch\f0 +ng those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the D +\hich\af0\dbch\af11\loch\f0 e\hich\af0\dbch\af11\loch\f0 +rivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attri +\hich\af0\dbch\af11\loch\f0 b\hich\af0\dbch\af11\loch\f0 +ution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. +\par }\pard \ltrpar\ql \li0\ri0\sb120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid9785620 {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 +You may add Your own copyright stateme\hich\af0\dbch\af11\loch\f0 +nt to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of t +\hich\af0\dbch\af11\loch\f0 h\hich\af0\dbch\af11\loch\f0 e Work otherwise complies with the conditions stated in this License. +\par }\pard \ltrpar\ql \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid9785620 {\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid9785620\charrsid12260020 {\*\bkmkstart contributions} +\hich\af0\dbch\af11\loch\f0 5. Submission of Contributions}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid9785620\charrsid12260020 {\*\bkmkend contributions}\hich\af0\dbch\af11\loch\f0 . Unless You explicitly stat\hich\af0\dbch\af11\loch\f0 +e otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licensor shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall sup +\hich\af0\dbch\af11\loch\f0 e\hich\af0\dbch\af11\loch\f0 rsede or modify the terms of any separate license agreement you may have executed with Licensor regarding such Contributions. +\par }{\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid9785620\charrsid12260020 {\*\bkmkstart trademarks}\hich\af0\dbch\af11\loch\f0 6. Trademarks}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid9785620\charrsid12260020 {\*\bkmkend trademarks} +\hich\af0\dbch\af11\loch\f0 . This License does not grant permission to use the trade names, trademarks, service marks, or p\hich\af0\dbch\af11\loch\f0 +roduct names of the Licensor, except as required for reasonable and customary use in describing the origin of the Work and reproducing the content of the NOTICE file. +\par }{\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid9785620\charrsid12260020 {\*\bkmkstart no-warranty}{\*\bkmkend no-warranty}\hich\af0\dbch\af11\loch\f0 7. Disclaimer of Warranty}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 +\f0\fs24\insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 . Unless required by applicable law or ag\hich\af0\dbch\af11\loch\f0 +reed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on an "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of T +\hich\af0\dbch\af11\loch\f0 I\hich\af0\dbch\af11\loch\f0 +TLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsible for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this Li +\hich\af0\dbch\af11\loch\f0 c\hich\af0\dbch\af11\loch\f0 ense. +\par }{\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid9785620\charrsid12260020 {\*\bkmkstart no-liability}{\*\bkmkend no-liability}\hich\af0\dbch\af11\loch\f0 8. Limitation of Liability}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 +\f0\fs24\insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 +. In no event and under no legal theory, whether in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to\hich\af0\dbch\af11\loch\f0 + in writing, shall any Contributor be liable to You for damages, including any direct, indirect, special, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including b +\hich\af0\dbch\af11\loch\f0 u\hich\af0\dbch\af11\loch\f0 +t not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or any and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. +\par }{\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid9785620\charrsid12260020 {\*\bkmkstart additional}\hich\af0\dbch\af11\loch\f0 9. Accepting Warrant\hich\af0\dbch\af11\loch\f0 y or Additional Liability}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 +\f0\fs24\insrsid9785620\charrsid12260020 {\*\bkmkend additional}\hich\af0\dbch\af11\loch\f0 +. While redistributing the Work or Derivative Works thereof, You may choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this Lic\hich\af0\dbch\af11\loch\f0 +ense. However, in accepting such obligations, You may act only on Your own behalf and on Your sole responsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability inc +\hich\af0\dbch\af11\loch\f0 u\hich\af0\dbch\af11\loch\f0 rred by, or claims asserted against, such Contributor by reason of your accepting any such warranty or additional liability. +\par \hich\af0\dbch\af11\loch\f0 See }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 + HYPERLINK "http://www.codeplex.com/IronPython/Wiki/View.aspx?title=FAQ&referringTitle=Home" }{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid9785620\charrsid12260020 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90bb800000068007400740070003a002f002f007700770077002e0063006f006400650070006c00650078002e0063006f006d002f00490072006f006e0050007900740068006f006e002f00570069006b0069002f005600 +6900650077002e0061007300700078003f007400690074006c0065003d00460041005100260072006500660065007200720069006e0067005400690074006c0065003d0048006f006d0065000000795881f43b1d7f48af2c825dc485276300000000a5ab0000}}}{\fldrslt {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 +\f0\fs24\ul\cf2\insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 FAQ}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid9785620\charrsid12260020 \hich\af0\dbch\af11\loch\f0 + for answers to frequently \hich\af0\dbch\af11\loch\f0 asked questions about this license.}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid9964378\charrsid9785620 +\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a +9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad +5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 +b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 +0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 +a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f +c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 +0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 +a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 +6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b +4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b +4757e8d3f729e245eb2b260a0238fd010000ffff0300504b03041400060008000000210096b5ade296060000501b0000160000007468656d652f7468656d652f +7468656d65312e786d6cec594f6fdb3614bf0fd87720746f6327761a07758ad8b19b2d4d1bc46e871e698996d850a240d2497d1bdae38001c3ba618715d86d87 +615b8116d8a5fb34d93a6c1dd0afb0475292c5585e9236d88aad3e2412f9e3fbff1e1fa9abd7eec70c1d1221294fda5efd72cd4324f1794093b0eddd1ef62fad +79482a9c0498f184b4bd2991deb58df7dfbb8ad755446282607d22d771db8b944ad79796a40fc3585ee62949606ecc458c15bc8a702910f808e8c66c69b9565b +5d8a314d3c94e018c8de1a8fa94fd05093f43672e23d06af89927ac06762a049136785c10607758d9053d965021d62d6f6804fc08f86e4bef210c352c144dbab +999fb7b4717509af678b985ab0b6b4ae6f7ed9ba6c4170b06c788a705430adf71bad2b5b057d03606a1ed7ebf5babd7a41cf00b0ef83a6569632cd467faddec9 +699640f6719e76b7d6ac355c7c89feca9cccad4ea7d36c65b258a206641f1b73f8b5da6a6373d9c11b90c537e7f08dce66b7bbeae00dc8e257e7f0fd2badd586 +8b37a088d1e4600ead1ddaef67d40bc898b3ed4af81ac0d76a197c86826828a24bb318f3442d8ab518dfe3a20f000d6458d104a9694ac6d88728eee2782428d6 +0cf03ac1a5193be4cbb921cd0b495fd054b5bd0f530c1931a3f7eaf9f7af9e3f45c70f9e1d3ff8e9f8e1c3e3073f5a42ceaa6d9c84e5552fbffdeccfc71fa33f +9e7ef3f2d117d57859c6fffac327bffcfc793510d26726ce8b2f9ffcf6ecc98baf3efdfdbb4715f04d814765f890c644a29be408edf3181433567125272371be +15c308d3f28acd249438c19a4b05fd9e8a1cf4cd296699771c393ac4b5e01d01e5a30a787d72cf1178108989a2159c77a2d801ee72ce3a5c545a6147f32a9979 +3849c26ae66252c6ed637c58c5bb8b13c7bfbd490a75330f4b47f16e441c31f7184e140e494214d273fc80900aedee52ead87597fa824b3e56e82e451d4c2b4d +32a423279a668bb6690c7e9956e90cfe766cb37b077538abd27a8b1cba48c80acc2a841f12e698f13a9e281c57911ce298950d7e03aba84ac8c154f8655c4f2a +f074481847bd804859b5e696007d4b4edfc150b12addbecba6b18b148a1e54d1bc81392f23b7f84137c2715a851dd0242a633f900710a218ed715505dfe56e86 +e877f0034e16bafb0e258ebb4faf06b769e888340b103d3311da9750aa9d0a1cd3e4efca31a3508f6d0c5c5c398602f8e2ebc71591f5b616e24dd893aa3261fb +44f95d843b5974bb5c04f4edafb95b7892ec1108f3f98de75dc97d5772bdff7cc95d94cf672db4b3da0a6557f70db629362d72bcb0431e53c6066acac80d699a +6409fb44d08741bdce9c0e4971624a2378cceaba830b05366b90e0ea23aaa241845368b0eb9e2612ca8c742851ca251ceccc70256d8d87265dd96361531f186c +3d9058edf2c00eafe8e1fc5c509031bb4d680e9f39a3154de0accc56ae644441edd76156d7429d995bdd88664a9dc3ad50197c38af1a0c16d684060441db0256 +5e85f3b9660d0713cc48a0ed6ef7dedc2dc60b17e92219e180643ed27acffba86e9c94c78ab90980d8a9f0913ee49d62b512b79626fb06dccee2a432bbc60276 +b9f7dec44b7904cfbca4f3f6443ab2a49c9c2c41476dafd55c6e7ac8c769db1bc399161ee314bc2e75cf8759081743be1236ec4f4d6693e5336fb672c5dc24a8 +c33585b5fb9cc24e1d4885545b58463634cc5416022cd19cacfccb4d30eb45296023fd35a458598360f8d7a4003bbaae25e331f155d9d9a5116d3bfb9a95523e +51440ca2e0088dd844ec6370bf0e55d027a012ae264c45d02f708fa6ad6da6dce29c255df9f6cae0ec38666984b372ab5334cf640b37795cc860de4ae2816e95 +b21be5ceaf8a49f90b52a51cc6ff3355f47e0237052b81f6800fd7b802239daf6d8f0b1571a8426944fdbe80c6c1d40e8816b88b8569082ab84c36ff0539d4ff +6dce591a26ade1c0a7f669880485fd484582903d284b26fa4e2156cff62e4b9265844c4495c495a9157b440e091bea1ab8aaf7760f4510eaa69a6465c0e04ec6 +9ffb9e65d028d44d4e39df9c1a52ecbd3607fee9cec7263328e5d661d3d0e4f62f44acd855ed7ab33cdf7bcb8ae889599bd5c8b3029895b6825696f6af29c239 +b75a5bb1e6345e6ee6c28117e73586c1a2214ae1be07e93fb0ff51e133fb65426fa843be0fb515c187064d0cc206a2fa926d3c902e907670048d931db4c1a449 +59d366ad93b65abe595f70a75bf03d616c2dd959fc7d4e6317cd99cbcec9c58b34766661c7d6766ca1a9c1b327531486c6f941c638c67cd22a7f75e2a37be0e8 +2db8df9f30254d30c1372581a1f51c983c80e4b71ccdd28dbf000000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468 +656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4 +350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d2624 +52282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe5141 +73d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c020000130000000000000000 +0000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b00000000000000 +000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c0000000000000000000000000019 +0200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d001400060008000000210096b5ade296060000501b00001600000000 +000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b01000027 +00000000000000000000000000a00900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d0100009b0a00000000} +{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d +617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 +6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 +656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} +{\*\latentstyles\lsdstimax267\lsdlockeddef0\lsdsemihiddendef1\lsdunhideuseddef1\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9; +\lsdpriority39 \lsdlocked0 toc 1;\lsdpriority39 \lsdlocked0 toc 2;\lsdpriority39 \lsdlocked0 toc 3;\lsdpriority39 \lsdlocked0 toc 4;\lsdpriority39 \lsdlocked0 toc 5;\lsdpriority39 \lsdlocked0 toc 6;\lsdpriority39 \lsdlocked0 toc 7; +\lsdpriority39 \lsdlocked0 toc 8;\lsdpriority39 \lsdlocked0 toc 9;\lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdpriority1 \lsdlocked0 Default Paragraph Font; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority59 \lsdlocked0 Table Grid;\lsdunhideused0 \lsdlocked0 Placeholder Text;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdunhideused0 \lsdlocked0 Revision; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdpriority37 \lsdlocked0 Bibliography;\lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;}}{\*\datastore 010500000200000018000000 +4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 +d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e50000000000000000000000009027 +7ca26a24cb01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools.csproj b/Tools/IronStudio/IronPythonTools/IronPythonTools.csproj new file mode 100644 index 0000000000..011304b165 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools.csproj @@ -0,0 +1,414 @@ + + + + + Debug + AnyCPU + 2.0 + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + Properties + Microsoft + IronPythonTools + v4.0 + {FA7BE5F5-E04F-4613-B7AC-70CE10D1BB68} + SAK + SAK + SAK + SAK + + + true + full + false + $(SolutionDir)..\bin\Debug + DEBUG;TRACE;$(SignedSym) + prompt + 4 + true + + + pdbonly + true + $(SolutionDir)..\bin\Release + TRACE;$(SignedSym) + prompt + 4 + true + + + + {B2E267A2-4836-40D7-817E-49D9D0E88435} + IronPythonToolsCore + + BuiltProjectOutputGroup + + + DebugSymbolsProjectOutputGroup + + False + + + {843716AE-38B3-4723-963C-950DD06BC4B8} + IronStudioCore + + + + + False + + + {C98DE16D-417E-4079-8939-1F8C8E28E2C8} + IronStudio + + + + + False + + + {155CE436-1669-4A48-8095-410F2430237F} + IronPython.Modules + + BuiltProjectOutputGroup + + + DebugSymbolsProjectOutputGroup + + + + {65E997B7-E99B-4C83-B29E-9951429BB293} + IronPython.Wpf + + BuiltProjectOutputGroup + + + DebugSymbolsProjectOutputGroup + + + + {95289EA9-5778-489D-AB48-F81F2CE2DA32} + IronPython %28Languages\IronPython\IronPython%29 + + BuiltProjectOutputGroup + + + DebugSymbolsProjectOutputGroup + + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Dynamic + + + + + False + + + {02FF0909-F5AD-48CF-A86A-345E721B7E40} + Microsoft.Scripting + + + + + False + + + + + False + + + False + + + False + + + False + + + + + + + + + + + + False + + + + + + False + $(DevEnvDir)\PublicAssemblies\Microsoft.VisualStudio.Language.NavigateTo.Interfaces.dll + + + + + global + + + + + + + + + + + + + + + False + + + False + C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Microsoft.Windows.Design.Host.dll + + + + + + + + + + + + + + + True + + + True + + + True + + + True + + + + + + + + + + + + + + + + + + + Component + + + Component + + + UserControl + + + PythonIntellisenseOptionsControl.cs + + + + UserControl + + + PythonOptionsControl.cs + + + Component + + + UserControl + + + PythonGeneralyPropertyPageControl.cs + + + + + + + + + + + + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + + + + + + Always + true + + + true + + + 1000 + Designer + + + Designer + + + + + + + + + + + + + + + Always + true + + + + + + + Always + true + + + + Always + true + + + Always + true + + + + + Always + true + + + + + Always + true + + + + + Always + true + + + + + + + + + + + Always + true + + + MSBuild:Compile + Designer + + + MSBuild:Compile + Designer + + + + + + + + + + + PythonIntellisenseOptionsControl.cs + + + PythonOptionsControl.cs + + + PythonGeneralyPropertyPageControl.cs + + + + + Designer + Microsoft.IronPythonTools + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + true + Microsoft.IronPythonTools + + + + + true + true + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools.csproj.vspscc b/Tools/IronStudio/IronPythonTools/IronPythonTools.csproj.vspscc new file mode 100644 index 0000000000..feffdecaa4 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools.vsct b/Tools/IronStudio/IronPythonTools/IronPythonTools.vsct new file mode 100644 index 0000000000..3e10f47d64 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools.vsct @@ -0,0 +1,197 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IronPython Tools + IronP&ython Tools + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/Command.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/Command.cs new file mode 100644 index 0000000000..4f8d2362e7 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/Command.cs @@ -0,0 +1,62 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio.OLE.Interop; + +namespace Microsoft.IronPythonTools.Commands { + /// + /// Base class for available commands. To add a new command you must first update the .vsct file so that + /// our commands are registered and available. Then you need to subclass Command and at a new instance of + /// the command in CommandTable. IronPythonToolsPackage will then register the command on startup. + /// + abstract class Command { + /// + /// Provides the implementation of what should happen when the command is executed. + /// + /// sender is the MenuCommand or OleMenuCommand object which is causing the event to be fired. + /// + public abstract void DoCommand(object sender, EventArgs args); + + /// + /// Enables a command to hook into our edit filter for Python text buffers. + /// + /// Called with the OLECMD object for the command being processed. Returns null + /// if the command does not want to handle this message or the HRESULT that + /// should be returned from the QueryStatus call. + /// + public virtual int? EditFilterQueryStatus(ref OLECMD cmd, IntPtr pCmdText) { + return null; + } + + /// + /// Provides the CommandId for this command which corresponds to the CommandId in the vsct file + /// and PkgCmdId.cs. + /// + public abstract int CommandId { + get; + } + + /// + /// Provides an event handler that will be invoked before the menu containing the command + /// is displayed. This can enable, disable, or hide the menu command. By default returns + /// null. + /// + public virtual EventHandler BeforeQueryStatus { + get { + return null; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/CommandTable.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/CommandTable.cs new file mode 100644 index 0000000000..e89e59c3a0 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/CommandTable.cs @@ -0,0 +1,28 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +namespace Microsoft.IronPythonTools.Commands { + /// + /// List of all commands defined for IronPython package + /// + static class CommandTable { + public static Command[] Commands = new Command[] { + new ExecuteInReplCommand(), + new OpenReplCommand(), + new SendToReplCommand(), + new SendToDefiningModuleCommand(), + new FillParagraphCommand() + }; + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/ExecuteInReplCommand.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/ExecuteInReplCommand.cs new file mode 100644 index 0000000000..d5e4c99c17 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/ExecuteInReplCommand.cs @@ -0,0 +1,137 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Linq; +using System.Threading; +using Microsoft.IronPythonTools.Library.Repl; +using Microsoft.IronPythonTools.Navigation; +using Microsoft.IronPythonTools.Project; +using Microsoft.IronPythonTools.Repl; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Repl; +using Microsoft.Scripting.Utils; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.IronPythonTools.Commands { + /// + /// Provides the command for starting a file or the start item of a project in the REPL window. + /// + internal sealed class ExecuteInReplCommand : Command { + public static ExecuteInReplCommand Instance; + private static Guid _replGuid = new Guid("{FAEC7F47-85D8-4899-8D7B-0B855B732CC8}"); + + public ExecuteInReplCommand() { + Instance = this; + } + + internal static VsReplWindow/*!*/ EnsureReplWindow() { + var compModel = IronPythonToolsPackage.ComponentModel; + var provider = compModel.GetExtensions().First(); + + var window = (VsReplWindow)provider.FindReplWindow(_replGuid); + if (window == null) { + var evaluator = new RemotePythonVsEvaluator(); + evaluator.Initialize(); + window = (VsReplWindow)provider.CreateReplWindow( + evaluator, + IronPythonToolsPackage.Instance.ContentType, + "IronPython Interactive", + typeof(PythonLanguageInfo).GUID, + _replGuid + ); + + window.UseSmartUpDown = IronPythonToolsPackage.Instance.OptionsPage.ReplSmartHistory; + } + return window; + } + + internal static VsReplWindow TryGetReplWindow() { + var compModel = IronPythonToolsPackage.ComponentModel; + var provider = compModel.GetExtensions(); + + return provider.First().FindReplWindow(_replGuid) as VsReplWindow; + } + + public override EventHandler BeforeQueryStatus { + get { + return QueryStatusMethod; + } + } + + private void QueryStatusMethod(object sender, EventArgs args) { + var oleMenu = sender as OleMenuCommand; + + IWpfTextView textView; + var pyProj = CommonPackage.GetStartupProject() as PythonProjectNode; + if (pyProj != null) { + // startup project, enabled in Start in REPL mode. + oleMenu.Visible = true; + oleMenu.Enabled = true; + oleMenu.Supported = true; + oleMenu.Text = "Execute Project in IronPython Interactive"; + } else if ((textView = CommonPackage.GetActiveTextView()) != null && + textView.TextBuffer.ContentType == IronPythonToolsPackage.Instance.ContentType) { + // enabled in Execute File mode... + oleMenu.Visible = true; + oleMenu.Enabled = true; + oleMenu.Supported = true; + oleMenu.Text = "Execute File in IronPython Interactive"; + } else { + oleMenu.Visible = false; + oleMenu.Enabled = false; + oleMenu.Supported = false; + } + } + + public override void DoCommand(object sender, EventArgs args) { + var window = (IReplWindow)EnsureReplWindow(); + IVsWindowFrame windowFrame = (IVsWindowFrame)((ToolWindowPane)window).Frame; + + string filename, dir; + if (!CommonPackage.TryGetStartupFileAndDirectory(out filename, out dir)) { + // TODO: Error reporting + return; + } + + ((RemotePythonVsEvaluator)window.Evaluator).Reset(); + + ErrorHandler.ThrowOnFailure(windowFrame.Show()); + window.Focus(); + + window.WriteLine(String.Format("Running {0}", filename)); + string scopeName = Path.GetFileNameWithoutExtension(filename); + // now execute the current file in the REPL + var engine = ((RemotePythonEvaluator)window.Evaluator).Engine; + ThreadPool.QueueUserWorkItem( + _ => { + try { + var src = engine.CreateScriptSourceFromFile(filename, StringUtils.DefaultEncoding, Scripting.SourceCodeKind.Statements); + src.Compile().Execute(((RemotePythonEvaluator)window.Evaluator).CurrentScope); + } catch (Exception e) { + window.WriteLine(String.Format("Exception: {0}", e)); + } + } + ); + } + + public override int CommandId { + get { return (int)PkgCmdIDList.cmdidExecuteFileInRepl; } + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/FillParagraphCommand.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/FillParagraphCommand.cs new file mode 100644 index 0000000000..7dfbb2ce4a --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/FillParagraphCommand.cs @@ -0,0 +1,353 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Text; +using System.Text.RegularExpressions; +using Microsoft.IronStudio; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Language.StandardClassification; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods; + +namespace Microsoft.IronPythonTools.Commands { + /// + /// Provides the command to send selected text from a buffer to the remote REPL window. + /// + class FillParagraphCommand : Command { + private const string _sentenceTerminators = ".!?"; + private static Regex _startDocStringRegex = new Regex("^[\t ]*('''|\"\"\")[\t ]*$"); + private static Regex _endDocStringRegex = new Regex("('''|\"\"\")[\t ]*$"); + private static Regex _commentRegex = new Regex("^[\t ]*#+[\t ]*"); + private static Regex _whitespaceRegex = new Regex("^[\t ]+"); + + public override void DoCommand(object sender, EventArgs args) { + FillCommentParagraph(CommonPackage.GetActiveTextView()); + } + + /// + /// FillCommentParagraph fills the text in a contiguous block of comment lines, + /// each having the same [whitespace][commentchars][whitespace] prefix. Each + /// resulting line is as long as possible without exceeding the + /// Config.CodeWidth.Width column. This function also works on paragraphs + /// within doc strings, each having the same leading whitespace. Leading + /// whitespace must be space characters. + /// + public void FillCommentParagraph(ITextView view) { + var caret = view.Caret; + var txtbuf = view.TextBuffer; + // don't clone Caret, need point that works at buffer level not view. + var bufpt = caret.Position.BufferPosition; //txtbuf.GetTextPoint(caret.CurrentPosition); + var fillPrefix = GetFillPrefix(view, bufpt); + + // TODO: Fix doc string parsing + if (fillPrefix.Prefix == null || fillPrefix.Prefix.Length == 0 || fillPrefix.IsDocString) { + System.Windows.MessageBox.Show("Must invoke FillCommentParagraph on a comment line or non-blank line."); + return; + } + + var start = FindParagraphStart(bufpt, fillPrefix); + var end = FindParagraphEnd(bufpt, fillPrefix); + string newLine = view.Options.GetNewLineCharacter(); + using (var edit = view.TextBuffer.CreateEdit()) { + int startLine = start.GetContainingLine().LineNumber; + string[] lines = new string[end.GetContainingLine().LineNumber - startLine + 1]; + for (int i = 0; i < lines.Length; i++) { + lines[i] = start.Snapshot.GetLineFromLineNumber(startLine + i).GetText(); + } + + int curLine = 0, curOffset = fillPrefix.Prefix.Length; + int columnCutoff = IronPythonToolsPackage.Instance.OptionsPage.FillParagraphColumns - fillPrefix.Prefix.Length; + int defaultColumnCutoff = columnCutoff; + StringBuilder newText = new StringBuilder(end.Position - start.Position); + while (curLine < lines.Length) { + string curLineText = lines[curLine]; + int lastSpace = curLineText.Length; + + // skip leading white space + while (curOffset < curLineText.Length && Char.IsWhiteSpace(curLineText[curOffset])) { + curOffset++; + } + + // find next word + for (int i = curOffset; i < curLineText.Length; i++) { + if (Char.IsWhiteSpace(curLineText[i])) { + lastSpace = i; + break; + } + } + + if (lastSpace - curOffset < columnCutoff || columnCutoff == defaultColumnCutoff) { + // we found a like break in the region and it's a reasonable size or + // we have a really long word that we need to append unbroken + if (columnCutoff == defaultColumnCutoff) { + // first time we're appending to this line + newText.Append(fillPrefix.Prefix); + } + + newText.Append(curLineText, curOffset, lastSpace - curOffset); + + // append appropriate spacing + if (_sentenceTerminators.IndexOf(curLineText[lastSpace - 1]) != -1 || // we end in punctuation + ((lastSpace - curOffset) > 1 && // we close a paren that ends in punctuation + curLineText[lastSpace - curOffset] == ')' && + _sentenceTerminators.IndexOf(curLineText[lastSpace - 2]) != -1)) { + + newText.Append(" "); + columnCutoff -= lastSpace - curOffset + 2; + } else { + newText.Append(' '); + columnCutoff -= lastSpace - curOffset + 1; + } + curOffset = lastSpace + 1; + } else { + // current word is too long to append. Start the next line. + while (newText.Length > 0 && newText[newText.Length - 1] == ' ') { + newText.Length = newText.Length - 1; + } + newText.Append(newLine); + columnCutoff = defaultColumnCutoff; + } + + if (curOffset >= lines[curLine].Length) { + // we're not reading from the next line + curLine++; + curOffset = fillPrefix.Prefix.Length; + } + } + while (newText.Length > 0 && newText[newText.Length - 1] == ' ') { + newText.Length = newText.Length - 1; + } + + // commit the new text + edit.Delete(start.Position, end.Position - start.Position); + edit.Insert(start.Position, newText.ToString()); + edit.Apply(); + } + + } + + private static int GetFirstNonWhiteSpaceCharacterOnLine(ITextSnapshotLine line) { + var text = line.GetText(); + for (int i = 0; i < text.Length; i++) { + if (!Char.IsWhiteSpace(text[i])) { + return line.Start.Position + i; + } + } + return line.End.Position; + } + + private bool PrevNotParaStart(FillPrefix prefix, int prev_num, int ln_num, string prev_txt, Regex regexp) { + var notBufFirstLn = prev_num != ln_num; + var notFileDocStrAndHasPrefix = prefix.Prefix != "" && prev_txt.IndexOf(prefix.Prefix) == 0; + var isFileDocStrAndNotEmptyLine = prefix.Prefix == "" && prev_txt != ""; + var notDocStringOrNoTripleQuotesYet = !(prefix.IsDocString && regexp.Match(prev_txt).Success); + return (notBufFirstLn && + (notFileDocStrAndHasPrefix || isFileDocStrAndNotEmptyLine) && + notDocStringOrNoTripleQuotesYet); + } + + // Finds first contiguous line that has the same prefix as the current line. + // If the prefix is not a comment start prefix, then this function stops at + // the first line with triple quotes or that is empty. It returns the point + // positioned at the start of the first line. + private SnapshotPoint FindParagraphStart(SnapshotPoint point, FillPrefix prefix) { + var buf = point.Snapshot.TextBuffer; + + Regex regexp = null; + if (prefix.IsDocString) { + regexp = new Regex(prefix + "('''|\"\"\")"); + // Check for edge case of being on first line of docstring with quotes. + if (regexp.Match(point.GetContainingLine().GetText()).Success) { + return point.GetContainingLine().Start; + } + } + + var line = point.GetContainingLine(); + var ln_num = line.LineNumber; + + var prev_num = ln_num - 1; + if (prev_num < 0) { + return line.Start; + } + var prev_txt = point.Snapshot.GetLineFromLineNumber(prev_num).GetText(); + + while (PrevNotParaStart(prefix, prev_num, ln_num, prev_txt, regexp)) { + ln_num = prev_num; + prev_num--; + if (prev_num < 0) { + return new SnapshotPoint(point.Snapshot, 0); + } + prev_txt = point.Snapshot.GetLineFromLineNumber(prev_num).GetText(); + } + + SnapshotPoint res; + if (!prefix.IsDocString || + _startDocStringRegex.Match(prev_txt).Success || + prev_txt == "") { + // Normally ln is the start for filling, prev line stopped the loop. + res = point.Snapshot.GetLineFromLineNumber(ln_num).Start; + } else { + // If we're in a doc string, and prev is not just the triple quotes + // line, and prev is not empty, then we use prev line because it + // has text on it we need to fill. Point is already on prev line. + res = point.Snapshot.GetLineFromLineNumber(prev_num).Start; + } + return res; + } + + private bool NextNotParaEnd(FillPrefix prefix, int next_num, int ln_num, string next_txt, Regex regexp) { + var notBufLastLn = next_num != ln_num; + var notFileDocStrAndHasPrefix = prefix.Prefix != "" && next_txt.IndexOf(prefix.Prefix) == 0; + var isFileDocStrAndNotEmptyLine = prefix.Prefix == "" && next_txt != ""; + var notDocStringOrNoTripleQuotesYet = !(prefix.IsDocString && regexp.Match(next_txt).Success); + return (notBufLastLn && + (notFileDocStrAndHasPrefix || isFileDocStrAndNotEmptyLine) && + notDocStringOrNoTripleQuotesYet); + } + + // Finds last contiguous line that has the same prefix as the current line. If + // the prefix is not a comment start prefix, then this function stops at the + // last line with triple quotes or that is empty. It returns the point + // positioned at the start of the last line. + private SnapshotPoint FindParagraphEnd(SnapshotPoint point, FillPrefix prefix) { + Regex regexp = null; + if (prefix.IsDocString) { + regexp = _endDocStringRegex; + // Check for edge case of being on last line of doc string with quotes. + if (regexp.Match(point.GetContainingLine().GetText()).Success) { + return point.GetContainingLine().Start; + } + } + + var line = point.GetContainingLine(); + var ln_num = line.LineNumber; + + var next_num = ln_num + 1; + if (next_num >= point.Snapshot.LineCount) { + return line.End; + } + + var next_txt = point.Snapshot.GetLineFromLineNumber(next_num).GetText(); + + while (NextNotParaEnd(prefix, next_num, ln_num, next_txt, regexp)) { + ln_num = next_num; + next_num++; + next_txt = point.Snapshot.GetLineFromLineNumber(next_num).GetText(); + } + + SnapshotPoint res; + if (!prefix.IsDocString || + _startDocStringRegex.Match(next_txt).Success || + next_txt == "") { + // Normally ln is the last line to fill, next line stopped the loop. + res = point.Snapshot.GetLineFromLineNumber(ln_num).End; + } else { + // If we're in a doc string, and next is not just the triple quotes + // line, and next is not empty, then we use next line because it has + // text on it we need to fill. Point is on next line. + res = point.Snapshot.GetLineFromLineNumber(next_num).End; + } + return res; + } + + struct FillPrefix { + public readonly string Prefix; + public readonly bool IsDocString; + + public FillPrefix(string prefix, bool isDocString) { + Prefix = prefix; + IsDocString = isDocString; + } + } + + /// + /// Returns the or the + /// prefix on point's line. Returns None if not on a suitable line for filling. + /// + private FillPrefix GetFillPrefix(ITextView textWindow, SnapshotPoint point) { + var regexp = _commentRegex; + + var line = point.GetContainingLine(); + var lntxt = point.GetContainingLine().GetText(); + var match = regexp.Match(lntxt); + if (match.Success) { + return new FillPrefix(lntxt.Substring(0, match.Length), false); + } else if (lntxt.Trim() == "") { + return new FillPrefix(null, false); + } else { + regexp = _whitespaceRegex; + match = regexp.Match(lntxt); + if (match.Success && IsDocString(textWindow, point)) { + return new FillPrefix(lntxt.Substring(0, match.Length), true); + } else if (/*GetFirstNonWhiteSpaceCharacterOnLine(line) == 0 && */IsDocString(textWindow, point)) { + return new FillPrefix("", true); + } else { + return new FillPrefix(null, false); + } + } + } + + private bool IsDocString(ITextView textWindow, SnapshotPoint point) { + var aggregator = CommonPackage.ComponentModel.GetService(); + IClassifier classifier = aggregator.GetClassifier(textWindow.TextBuffer); + + var curLine = point.GetContainingLine(); + var tokens = classifier.GetClassificationSpans(curLine.Extent); + // TODO: Is null the right check for not having tokens? + for (int i = curLine.LineNumber - 1; i >= 0 && tokens == null; i--) { + tokens = classifier.GetClassificationSpans(curLine.Extent); + if (tokens != null) { + break; + } + i = i - 1; + } + if (tokens == null) { + return false; + } + // Tokens is NOT None here. + // If first token found on a line is only token and is string literal, + // we're in a doc string. Because multiline, can't be "" or ''. + return tokens.Count == 1 && tokens[0].ClassificationType.IsOfType(PredefinedClassificationTypeNames.String); + } + + public override int? EditFilterQueryStatus(ref VisualStudio.OLE.Interop.OLECMD cmd, IntPtr pCmdText) { + var activeView = CommonPackage.GetActiveTextView(); + if (activeView != null && activeView.TextBuffer.ContentType.IsOfType(PythonCoreConstants.ContentType)) { + cmd.cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); + } else { + cmd.cmdf = (uint)(OLECMDF.OLECMDF_INVISIBLE); + } + + return VSConstants.S_OK; + } + + public override EventHandler BeforeQueryStatus { + get { + return (sender, args) => { + ((OleMenuCommand)sender).Visible = false; + ((OleMenuCommand)sender).Supported = false; + }; + } + } + + public override int CommandId { + get { return (int)PkgCmdIDList.cmdidFillParagraph; } + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/OpenReplCommand.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/OpenReplCommand.cs new file mode 100644 index 0000000000..f4fffe0c38 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/OpenReplCommand.cs @@ -0,0 +1,40 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.IronPythonTools.Library.Repl; +using Microsoft.IronStudio.Repl; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.IronPythonTools.Commands { + /// + /// Provides the command for starting the IronPython REPL window. + /// + class OpenReplCommand : Command { + private static Guid _replGuid = new Guid("{ABF3BBD6-DA27-468D-B169-B29243A757C5}"); + + public override void DoCommand(object sender, EventArgs args) { + var window = ExecuteInReplCommand.EnsureReplWindow(); + + IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame; + ErrorHandler.ThrowOnFailure(windowFrame.Show()); + ((IReplWindow)window).Focus(); + } + + public override int CommandId { + get { return (int)PkgCmdIDList.cmdidReplWindow; } + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/SendToDefiningModuleCommand.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/SendToDefiningModuleCommand.cs new file mode 100644 index 0000000000..9c3f422fbb --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/SendToDefiningModuleCommand.cs @@ -0,0 +1,54 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.IronPythonTools.Library.Repl; +using Microsoft.IronPythonTools.Internal; + +namespace Microsoft.IronPythonTools.Commands { + class SendToDefiningModuleCommand : SendToReplCommand { + public override void DoCommand(object sender, EventArgs args) { + var window = ExecuteInReplCommand.EnsureReplWindow(); + var eval = window.Evaluator as RemotePythonEvaluator; + var activeView = IronPythonToolsPackage.GetActiveTextView(); + + string path = activeView.GetFilePath(); + if (path != null) { + var scope = eval.Engine.GetScope(path); + if (scope != null) { + // we're now in the correct module, execute the code + string scopeName = eval.SetScope(scope); + window.Cancel(); + if (scopeName != String.Empty) { + window.WriteLine(eval.Prompt + " %module " + scopeName); + window.WriteLine(String.Format("Current scope changed to {0}", scopeName)); + } else { + window.WriteLine(eval.Prompt + " %module (unknown name)"); + window.WriteLine("Current scope changed to (unknown name)"); + } + + base.DoCommand(sender, args); + } else { + window.WriteLine(String.Format("Could not find module: {0}", path)); + } + } else { + window.WriteLine("Could not find module"); + } + } + + public override int CommandId { + get { return (int)PkgCmdIDList.cmdidSendToDefiningModule; } + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/SendToReplCommand.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/SendToReplCommand.cs new file mode 100644 index 0000000000..925fb5c898 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Commands/SendToReplCommand.cs @@ -0,0 +1,73 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Repl; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.IronPythonTools.Commands { + /// + /// Provides the command to send selected text from a buffer to the remote REPL window. + /// + class SendToReplCommand : Command { + public override void DoCommand(object sender, EventArgs args) { + ToolWindowPane window = ExecuteInReplCommand.EnsureReplWindow(); + + IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame; + ErrorHandler.ThrowOnFailure(windowFrame.Show()); + + var activeView = CommonPackage.GetActiveTextView(); + + foreach (var span in activeView.Selection.SelectedSpans) { + var text = span.GetText(); + ((IReplWindow)window).PasteText(text); + } + + ((IReplWindow)window).Focus(); + } + + public override int? EditFilterQueryStatus(ref VisualStudio.OLE.Interop.OLECMD cmd, IntPtr pCmdText) { + var activeView = CommonPackage.GetActiveTextView(); + if (activeView != null && activeView.TextBuffer.ContentType.IsOfType(PythonCoreConstants.ContentType)) { + if (activeView.Selection.IsEmpty || activeView.Selection.Mode == TextSelectionMode.Box) { + cmd.cmdf = (uint)(OLECMDF.OLECMDF_SUPPORTED); + } else { + cmd.cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); + } + } else { + cmd.cmdf = (uint)(OLECMDF.OLECMDF_INVISIBLE); + } + + return VSConstants.S_OK; + } + + public override EventHandler BeforeQueryStatus { + get { + return (sender, args) => { + ((OleMenuCommand)sender).Visible = false; + ((OleMenuCommand)sender).Supported = false; + }; + } + } + + public override int CommandId { + get { return (int)PkgCmdIDList.cmdidSendToRepl; } + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Designer/PythonRuntimeNameProvider.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Designer/PythonRuntimeNameProvider.cs new file mode 100644 index 0000000000..1673208b22 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Designer/PythonRuntimeNameProvider.cs @@ -0,0 +1,90 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using System.ComponentModel; +using System.Globalization; +using System.CodeDom.Compiler; +using Microsoft.Windows.Design.Host; + +namespace Microsoft.IronPythonTools.Designer { +#if FALSE + class PythonRuntimeNameProvider : RuntimeNameProvider { + public override string CreateValidName(string proposal) { + return proposal; + } + + public override bool IsExistingName(string name) { + //We will get uniqueness in the XAML file via the matchScope predicate. + //In a more complete implementation, this method would verify that there isn't + //a member in the code behind file with the given name. + return false; + } + + public override RuntimeNameFactory NameFactory { + get { return new PythonRuntimeNameFactory(); } + } + } + + [Serializable] + public class PythonRuntimeNameFactory : RuntimeNameFactory { + public override string CreateUniqueName(Type itemType, string proposedName, Predicate matchScope, bool rootScope, RuntimeNameProvider provider) { + if (null == itemType) throw new ArgumentNullException("itemType"); + if (null == matchScope) throw new ArgumentNullException("matchScope"); + if (null == provider) throw new ArgumentNullException("provider"); + + string name = null; + string baseName = proposedName; + + if (string.IsNullOrEmpty(baseName)) { + baseName = TypeDescriptor.GetClassName(itemType); + int lastDot = baseName.LastIndexOf('.'); + if (lastDot != -1) { + baseName = baseName.Substring(lastDot + 1); + } + + // Names should start with a lower-case character + baseName = char.ToLower(baseName[0], CultureInfo.InvariantCulture) + baseName.Substring(1); + } + + int idx = 1; + bool isUnique = false; + while (!isUnique) { + name = string.Format(CultureInfo.InvariantCulture, "{0}{1}", baseName, idx++); + + // Test for uniqueness + isUnique = !matchScope(name); + + string tempName = name; + name = provider.CreateValidName(tempName); + + if (!string.Equals(name, tempName, StringComparison.Ordinal)) { + // RNP has changed the name, test again for uniqueness + isUnique = !matchScope(name); + } + + if (isUnique && rootScope) { + // Root name scope means we have to let the RNP test for uniqueness too + isUnique = !provider.IsExistingName(name); + } + } + + return name; + } + } +#endif +} + diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Designer/WpfEventBindingProvider.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Designer/WpfEventBindingProvider.cs new file mode 100644 index 0000000000..724dadf173 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Designer/WpfEventBindingProvider.cs @@ -0,0 +1,203 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis; +using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods; +using Microsoft.Windows.Design.Host; + +namespace Microsoft.IronPythonTools.Designer { + class WpfEventBindingProvider : EventBindingProvider { + private Project.PythonFileNode _pythonFileNode; + + public WpfEventBindingProvider(Project.PythonFileNode pythonFileNode) { + _pythonFileNode = pythonFileNode; + } + + public override bool AddEventHandler(EventDescription eventDescription, string objectName, string methodName) { + // we return false here which causes the event handler to always be wired up via XAML instead of via code. + return false; + } + + public override bool AllowClassNameForMethodName() { + return true; + } + + public override void AppendStatements(EventDescription eventDescription, string methodName, string statements, int relativePosition) { + throw new NotImplementedException(); + } + + public override string CodeProviderLanguage { + get { return "Python"; } + } + + public override bool CreateMethod(EventDescription eventDescription, string methodName, string initialStatements) { + // build the new method handler + var view = _pythonFileNode.GetTextView(); + var textBuffer = _pythonFileNode.GetTextBuffer(); + var classDef = GetClassForEvents(); + if (classDef != null) { + int end = classDef.Body.EndIndex; + + using (var edit = textBuffer.CreateEdit()) { + var text = BuildMethod( + eventDescription, + methodName, + new string(' ', classDef.Body.Start.Column - 1), + view.Options.IsConvertTabsToSpacesEnabled() ? + view.Options.GetIndentSize() : + -1); + + edit.Insert(end, text); + edit.Apply(); + return true; + } + } + + + return false; + } + + private ClassDefinition GetClassForEvents() { + var analysis = _pythonFileNode.GetAnalysis() as IPythonProjectEntry; + + if (analysis != null) { + // TODO: Wait for up to date analysis + var suiteStmt = analysis.Tree.Body as SuiteStatement; + foreach (var stmt in suiteStmt.Statements) { + var classDef = stmt as ClassDefinition; + // TODO: Make sure this is the right class + if (classDef != null) { + return classDef; + } + } + } + return null; + } + + private static string BuildMethod(EventDescription eventDescription, string methodName, string indentation, int tabSize) { + StringBuilder text = new StringBuilder(); + text.AppendLine(); + text.AppendLine(indentation); + text.Append(indentation); + text.Append("def "); + text.Append(methodName); + text.Append('('); + text.Append("self"); + foreach (var param in eventDescription.Parameters) { + text.Append(", "); + text.Append(param.Name); + } + text.AppendLine("):"); + if (tabSize < 0) { + text.Append(indentation); + text.Append("\tpass"); + } else { + text.Append(indentation); + text.Append(' ', tabSize); + text.Append("pass"); + } + text.AppendLine(); + + return text.ToString(); + } + + public override string CreateUniqueMethodName(string objectName, EventDescription eventDescription) { + return string.Format(System.Globalization.CultureInfo.InvariantCulture, "{0}_{1}", objectName, eventDescription.Name); + } + + public override IEnumerable GetCompatibleMethods(EventDescription eventDescription) { + var classDef = GetClassForEvents(); + SuiteStatement suite = classDef.Body as SuiteStatement; + + if (suite != null) { + foreach (var methodCandidate in suite.Statements) { + FunctionDefinition funcDef = methodCandidate as FunctionDefinition; + if (funcDef != null) { + if (funcDef.Name.EndsWith("_" + eventDescription.Name)) { + yield return funcDef.Name; + } + } + } + } + } + + public override IEnumerable GetMethodHandlers(EventDescription eventDescription, string objectName) { + return new string[0]; + } + + public override bool IsExistingMethodName(EventDescription eventDescription, string methodName) { + var classDef = GetClassForEvents(); + var view = _pythonFileNode.GetTextView(); + SuiteStatement suite = classDef.Body as SuiteStatement; + + if (suite != null) { + foreach (var methodCandidate in suite.Statements) { + FunctionDefinition funcDef = methodCandidate as FunctionDefinition; + if (funcDef != null) { + if (funcDef.Name == methodName) { + return true; + } + } + } + } + + return false; + + } + + public override bool RemoveEventHandler(EventDescription eventDescription, string objectName, string methodName) { + throw new NotImplementedException(); + } + + public override bool RemoveHandlesForName(string elementName) { + throw new NotImplementedException(); + } + + public override bool RemoveMethod(EventDescription eventDescription, string methodName) { + throw new NotImplementedException(); + } + + public override void SetClassName(string className) { + throw new NotImplementedException(); + } + + public override bool ShowMethod(EventDescription eventDescription, string methodName) { + var classDef = GetClassForEvents(); + var view = _pythonFileNode.GetTextView(); + SuiteStatement suite = classDef.Body as SuiteStatement; + + if (suite != null) { + foreach (var methodCandidate in suite.Statements) { + FunctionDefinition funcDef = methodCandidate as FunctionDefinition; + if (funcDef != null) { + if (funcDef.Name == methodName) { + view.Caret.MoveTo(new VisualStudio.Text.SnapshotPoint(view.TextSnapshot, funcDef.StartIndex)); + view.Caret.EnsureVisible(); + return true; + } + } + } + } + + return false; + } + + public override void ValidateMethodName(EventDescription eventDescription, string methodName) { + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Editor/LanguagePreferences.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Editor/LanguagePreferences.cs new file mode 100644 index 0000000000..f6e8476824 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Editor/LanguagePreferences.cs @@ -0,0 +1,110 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.TextManager.Interop; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Editor; +using Microsoft.IronPythonTools.Navigation; + +namespace Microsoft.IronPythonTools.Editor { + class LanguagePreferences : IVsTextManagerEvents2 { + LANGPREFERENCES _preferences; + + public LanguagePreferences(LANGPREFERENCES preferences) { + _preferences = preferences; + } + + #region IVsTextManagerEvents2 Members + + public int OnRegisterMarkerType(int iMarkerType) { + return VSConstants.S_OK; + } + + public int OnRegisterView(IVsTextView pView) { + return VSConstants.S_OK; + } + + public int OnReplaceAllInFilesBegin() { + return VSConstants.S_OK; + } + + public int OnReplaceAllInFilesEnd() { + return VSConstants.S_OK; + } + + public int OnUnregisterView(IVsTextView pView) { + return VSConstants.S_OK; + } + + public int OnUserPreferencesChanged2(VIEWPREFERENCES2[] pViewPrefs, FRAMEPREFERENCES2[] pFramePrefs, LANGPREFERENCES2[] pLangPrefs, FONTCOLORPREFERENCES2[] pColorPrefs) { + if (pLangPrefs != null) { + _preferences.IndentStyle = pLangPrefs[0].IndentStyle; + _preferences.fAutoListMembers = pLangPrefs[0].fAutoListMembers; + _preferences.fAutoListParams = pLangPrefs[0].fAutoListParams; + if (_preferences.fDropdownBar != (_preferences.fDropdownBar = pLangPrefs[0].fDropdownBar)) { + CodeWindowManager.ToggleNavigationBar(_preferences.fDropdownBar != 0); + } + if (_preferences.fHideAdvancedAutoListMembers != (_preferences.fHideAdvancedAutoListMembers = pLangPrefs[0].fHideAdvancedAutoListMembers)) { + IronPythonToolsPackage.Instance.RuntimeHost.HideAdvancedMembers = HideAdvancedMembers; + } + } + return VSConstants.S_OK; + } + + #endregion + + #region Options + + public vsIndentStyle IndentMode { + get { + return _preferences.IndentStyle; + } + } + + public bool NavigationBar { + get { + // TODO: When this value changes we need to update all our views + return _preferences.fDropdownBar != 0; + } + } + + public bool HideAdvancedMembers { + get { + return _preferences.fHideAdvancedAutoListMembers != 0; + } + } + + public bool AutoListMembers { + get { + return _preferences.fAutoListMembers != 0; + } + } + + public bool AutoListParams { + get { + return _preferences.fAutoListParams != 0; + } + } + + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Editor/TextViewCreationListener.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Editor/TextViewCreationListener.cs new file mode 100644 index 0000000000..7b217e8ded --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Editor/TextViewCreationListener.cs @@ -0,0 +1,42 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel.Composition; +using Microsoft.IronPythonTools.Editor.Core; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronPythonTools.Editor { + /// + /// Watches for text views to be created for IronPython code. Then wires up whatever event handling + /// we're interested in. Currently this just handles highlighting matching braces but we should replace + /// our CodeWindowManager created via our language service and instead just use this. + /// + [Export(typeof(IVsTextViewCreationListener))] + [TextViewRole(PredefinedTextViewRoles.Editable)] + [ContentType(PythonCoreConstants.ContentType)] + class TextViewCreationListener : IVsTextViewCreationListener { + [Import] + internal IVsEditorAdaptersFactoryService AdapterService = null; + + public void VsTextViewCreated(VisualStudio.TextManager.Interop.IVsTextView textViewAdapter) { + ITextView textView = AdapterService.GetWpfTextView(textViewAdapter); + if (textView != null) { + BraceMatcher.WatchBraceHighlights(textView, IronPythonToolsPackage.ComponentModel); + } + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Editor/XamlTextViewCreationListener.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Editor/XamlTextViewCreationListener.cs new file mode 100644 index 0000000000..378333b54c --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Editor/XamlTextViewCreationListener.cs @@ -0,0 +1,43 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel.Composition; +using Microsoft.IronPythonTools.Intellisense; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronPythonTools.Editor { + /// + /// Watches for text views to be created for xaml code. Then wires up to support analysis so that + /// we can use the analysis for completion in .py code. + /// + [Export(typeof(IVsTextViewCreationListener))] + [TextViewRole(PredefinedTextViewRoles.Editable)] + [ContentType("xaml")] + class XamlTextViewCreationListener : IVsTextViewCreationListener { + [Import] + internal IVsEditorAdaptersFactoryService AdapterService = null; // Set by MEF + [Import] + internal IPythonAnalyzer PythonAnalyzer = null; // Set by MEF + + public void VsTextViewCreated(VisualStudio.TextManager.Interop.IVsTextView textViewAdapter) { + // TODO: We should probably only track text views in Python projects or loose files. + ITextView textView = AdapterService.GetWpfTextView(textViewAdapter); + if (textView != null) { + PythonAnalyzer.AnalyzeTextView(textView); + } + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Extensions.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Extensions.cs new file mode 100644 index 0000000000..6f3076fc30 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Extensions.cs @@ -0,0 +1,63 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.IronPythonTools.Intellisense; +using Microsoft.IronPythonTools.Project; +using Microsoft.IronStudio; +using Microsoft.PyAnalysis; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronPythonTools { + static class Extensions { + internal static void GotoSource(this LocationInfo location) { + IronPythonToolsPackage.NavigateTo( + location.FilePath, + Guid.Empty, + location.Line - 1, + location.Column - 1); + } + + internal static bool TryGetAnalysis(this ITextBuffer buffer, out IProjectEntry analysis) { + return buffer.Properties.TryGetProperty(typeof(IProjectEntry), out analysis); + } + + internal static IProjectEntry GetAnalysis(this ITextBuffer buffer) { + IProjectEntry res; + buffer.Properties.TryGetProperty(typeof(IProjectEntry), out res); + return res; + } + + internal static EnvDTE.Project GetProject(this IVsHierarchy hierarchy) { + object project; + + ErrorHandler.ThrowOnFailure( + hierarchy.GetProperty( + VSConstants.VSITEMID_ROOT, + (int)__VSHPROPID.VSHPROPID_ExtObject, + out project + ) + ); + + return (project as EnvDTE.Project); + } + + internal static PythonProjectNode GetPythonProject(this EnvDTE.Project project) { + return project.GetCommonProject() as PythonProjectNode; + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Intellisense/IntellisenseController.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Intellisense/IntellisenseController.cs new file mode 100644 index 0000000000..0c7e0af8f5 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Intellisense/IntellisenseController.cs @@ -0,0 +1,468 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Windows.Input; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.IncrementalSearch; +using Microsoft.VisualStudio.Text.Operations; +using VSConstants = Microsoft.VisualStudio.VSConstants; + +namespace Microsoft.IronPythonTools.Intellisense { + + class IntellisenseController : IIntellisenseController, IOleCommandTarget { + private readonly ITextView _textView; + private readonly IList _subjectBuffers; + private readonly IntellisenseControllerProvider _provider; + private readonly IIncrementalSearch _incSearch; + private ICompletionSession _activeSession; + private ISignatureHelpSession _sigHelpSession; + private IQuickInfoSession _quickInfoSession; + private IOleCommandTarget _oldTarget; + private IEditorOperations _editOps; + private IntellisensePreKeyProcessor _preProcessor; + + /// + /// Attaches events for invoking Statement completion + /// + /// + /// + /// + public IntellisenseController(IntellisenseControllerProvider provider, IList subjectBuffers, ITextView textView) { + _subjectBuffers = subjectBuffers; + _textView = textView; + _provider = provider; + _editOps = provider._EditOperationsFactory.GetEditorOperations(textView); + _incSearch = provider._IncrementalSearch.GetIncrementalSearch(textView); + _textView.MouseHover += new EventHandler(TextViewMouseHover); + textView.Properties.AddProperty(typeof(IntellisenseController), this); // added so our key processors can get back to us + } + + private void TextViewMouseHover(object sender, MouseHoverEventArgs e) { + if (_quickInfoSession != null && !_quickInfoSession.IsDismissed) { + _quickInfoSession.Dismiss(); + } + + _quickInfoSession = _provider._QuickInfoBroker.TriggerQuickInfo(_textView, _textView.TextSnapshot.CreateTrackingPoint(e.Position, PointTrackingMode.Negative), true); + } + + public void ConnectSubjectBuffer(ITextBuffer subjectBuffer) { + } + + public void DisconnectSubjectBuffer(ITextBuffer subjectBuffer) { + } + + /// + /// Detaches the events + /// + /// + public void Detach(ITextView textView) { + if (_textView == null) { + throw new InvalidOperationException("Already detached from text view"); + } + if (textView != _textView) { + throw new ArgumentException("Not attached to specified text view", "textView"); + } + + DetachKeyboardFilter(); + + if (_preProcessor != null) { + _preProcessor.PreprocessTextInput -= OnPreprocessKeyDown; + } + } + + /// + /// Triggers Statement completion when appropriate keys are pressed + /// The key combination is CTRL-J or "." + /// The intellisense window is dismissed when one presses ESC key + /// + /// + /// + private void OnPreprocessKeyDown(object sender, TextCompositionEventArgs e) { + // We should only receive pre-process events from our text view + Debug.Assert(sender == _textView); + + // TODO: We should handle = for signature completion of keyword arguments + + // We trigger completions when the user types . or space. Our EditFilter will + // also trigger completions when we receive the VSConstants.VSStd2KCmdID.SHOWMEMBERLIST + // command or the VSConstants.VSStd2KCmdID.COMPLETEWORD command. + // + // We trigger signature help when we receive a "(". We update our current sig when + // we receive a "," and we close sig help when we receive a ")". + if (!_incSearch.IsActive) { + switch (e.Text) { + case ".": + case " ": + if (IronPythonToolsPackage.Instance.LangPrefs.AutoListMembers) { + TriggerCompletionSession(false); + } + break; + case "(": + if (IronPythonToolsPackage.Instance.LangPrefs.AutoListParams) { + OpenParenStartSignatureSession(); + } + break; + case ")": + if (_sigHelpSession != null) { + _sigHelpSession.Dismiss(); + _sigHelpSession = null; + } + + if (IronPythonToolsPackage.Instance.LangPrefs.AutoListParams) { + // trigger help for outer call if there is one + TriggerSignatureHelp(); + } + break; + case ",": + if (_sigHelpSession == null) { + if (IronPythonToolsPackage.Instance.LangPrefs.AutoListParams) { + CommaStartSignatureSession(); + } + } else { + UpdateCurrentParameter(); + } + break; + } + } + } + + private bool Backspace() { + if (_sigHelpSession != null) { + if (_textView.Selection.IsActive && !_textView.Selection.IsEmpty) { + // when deleting a selection don't do anything to pop up signature help again + _sigHelpSession.Dismiss(); + return false; + } + + SnapshotPoint? caretPoint = GetCaretPoint(); + if (caretPoint != null && caretPoint.Value.Position != 0) { + string deleting = _textView.TextSnapshot.GetText(caretPoint.Value.Position - 1, 1); + if (deleting == ",") { + PythonSignature sig = _sigHelpSession.SelectedSignature as PythonSignature; + if (sig != null) { + int curParam = sig.Parameters.IndexOf(sig.CurrentParameter); + + if (curParam > 0) { + sig.SetCurrentParameter(sig.Parameters[curParam - 1]); + } + } + } else if (deleting == "(") { + _sigHelpSession.Dismiss(); + // delete the ( before triggering help again + _textView.TextSnapshot.TextBuffer.Delete(new Span(caretPoint.Value.Position - 1, 1)); + + // + // Pop to an outer nesting of signature help + if (IronPythonToolsPackage.Instance.LangPrefs.AutoListParams) { + TriggerSignatureHelp(); + } + + return true; + + } + } + } + return false; + } + + private void OpenParenStartSignatureSession() { + if (_activeSession != null) { + _activeSession.Dismiss(); + } + if (_sigHelpSession != null) { + _sigHelpSession.Dismiss(); + } + + TriggerSignatureHelp(); + } + + private void CommaStartSignatureSession() { + TriggerSignatureHelp(); + } + + /// + /// Updates the current parameter for the caret's current position. + /// + private void UpdateCurrentParameter() { + UpdateCurrentParameter(_textView.Caret.Position.BufferPosition.Position); + } + + /// + /// Updates the current parameter assuming the caret is at the provided position. + /// + /// This will analyze the buffer for where we are currently located, find the current + /// parameter that we're entering, and then update the signature. If our current + /// signature does not have enough parameters we'll find a signature which does. + /// + private void UpdateCurrentParameter(int position) { + // we advance to the next parameter + // TODO: Take into account params arrays + // TODO: need to parse and see if we have keyword arguments entered into the current signature yet + PythonSignature sig = _sigHelpSession.SelectedSignature as PythonSignature; + if (sig != null) { + var textBuffer = _textView.TextBuffer; + position = Math.Min(position, textBuffer.CurrentSnapshot.Length); + var span = textBuffer.CurrentSnapshot.CreateTrackingSpan(position, 0, SpanTrackingMode.EdgeInclusive); + + var sigs = _provider._Analyzer.GetSignatures(textBuffer.CurrentSnapshot, textBuffer, span); + int curParam = sigs.ParameterIndex; + + if (curParam < sig.Parameters.Count) { + sig.SetCurrentParameter(sig.Parameters[curParam]); + } else { + CommaFindBestSignature(curParam); + } + } + } + + private void CommaFindBestSignature(int curParam) { + // see if we have a signature which accomodates this... + + // TODO: We should also take into account param arrays + // TODO: We should also get the types of the arguments and use that to + // pick the best signature when the signature includes types. + foreach (var availableSig in _sigHelpSession.Signatures) { + if (availableSig.Parameters.Count > curParam) { + _sigHelpSession.SelectedSignature = availableSig; + + PythonSignature sig = availableSig as PythonSignature; + if (sig != null) { + sig.SetCurrentParameter(sig.Parameters[curParam]); + } + break; + } + } + } + + internal void TriggerCompletionSession(bool completeWord) { + Dismiss(); + + _activeSession = CompletionBroker.TriggerCompletion(_textView); + + if (_activeSession != null) { + if (completeWord && + _activeSession.CompletionSets.Count == 1 && + _activeSession.CompletionSets[0].Completions.Count == 1) { + _activeSession.Commit(); + } else { + AttachKeyboardFilter(); + _activeSession.Dismissed += new EventHandler(OnCompletionSessionDismissedOrCommitted); + _activeSession.Committed += new EventHandler(OnCompletionSessionDismissedOrCommitted); + } + } + } + + private void TriggerSignatureHelp() { + Debug.Assert(_sigHelpSession == null); + _sigHelpSession = SignatureBroker.TriggerSignatureHelp(_textView); + + if (_sigHelpSession != null) { + AttachKeyboardFilter(); + _sigHelpSession.Dismissed += new EventHandler(OnSignatureSessionDismissed); + + ISignature sig; + if (_sigHelpSession.Properties.TryGetProperty(typeof(PythonSignature), out sig)) { + _sigHelpSession.SelectedSignature = sig; + + IParameter param; + if (_sigHelpSession.Properties.TryGetProperty(typeof(PythonParameter), out param)) { + ((PythonSignature)sig).SetCurrentParameter(param); + } + } + } + } + + private void OnCompletionSessionDismissedOrCommitted(object sender, System.EventArgs e) { + // We've just been told that our active session was dismissed. We should remove all references to it. + _activeSession.Committed -= OnCompletionSessionDismissedOrCommitted; + _activeSession.Dismissed -= OnCompletionSessionDismissedOrCommitted; + _activeSession = null; + } + + private void OnSignatureSessionDismissed(object sender, System.EventArgs e) { + // We've just been told that our active session was dismissed. We should remove all references to it. + _sigHelpSession.Dismissed -= OnSignatureSessionDismissed; + _sigHelpSession = null; + } + + private SnapshotPoint? GetCaretPoint() { + return _textView.Caret.Position.Point.GetPoint( + textBuffer => _subjectBuffers.Contains(textBuffer), + PositionAffinity.Predecessor + ); + } + + private void DeleteSelectedSpans() { + if (!_textView.Selection.IsEmpty) { + _editOps.Delete(); + } + } + + private void Dismiss() { + if (_activeSession != null) { + _activeSession.Dismiss(); + } + } + + internal ICompletionBroker CompletionBroker { + get { + return _provider._CompletionBroker; + } + } + + internal IVsEditorAdaptersFactoryService AdaptersFactory { + get { + return _provider._adaptersFactory; + } + } + + internal ISignatureHelpBroker SignatureBroker { + get { + return _provider._SigBroker; + } + } + + #region Internal Implementation + + /// + /// Attaches two events + /// + internal void Attach(IntellisensePreKeyProcessor preProcessor) { + preProcessor.PreprocessTextInput += OnPreprocessKeyDown; + _preProcessor = preProcessor; + } + + #endregion + + #region IOleCommandTarget Members + + // we need this because VS won't give us certain keyboard events as they're handled before our key processor. These + // include enter and tab both of which we want to complete. + + private void AttachKeyboardFilter() { + if (_oldTarget == null) { + ErrorHandler.ThrowOnFailure(AdaptersFactory.GetViewAdapter(_textView).AddCommandFilter(this, out _oldTarget)); + } + } + + private void DetachKeyboardFilter() { + ErrorHandler.ThrowOnFailure(AdaptersFactory.GetViewAdapter(_textView).RemoveCommandFilter(this)); + } + + public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { + if (_activeSession != null) { + if (pguidCmdGroup == VSConstants.VSStd2K) { + switch ((VSConstants.VSStd2KCmdID)nCmdID) { + case VSConstants.VSStd2KCmdID.RETURN: + if (IronPythonToolsPackage.Instance.IntellisenseOptionsPage.EnterCommitsIntellisense && + !_activeSession.IsDismissed && + _activeSession.SelectedCompletionSet.SelectionStatus.IsSelected) { + + // If the user has typed all of the characters as the completion and presses + // enter we should dismiss & let the text editor receive the enter. For example + // when typing "import sys[ENTER]" completion starts after the space. After typing + // sys the user wants a new line and doesn't want to type enter twice. + + bool enterOnComplete = IronPythonToolsPackage.Instance.IntellisenseOptionsPage.AddNewLineAtEndOfFullyTypedWord && + EnterOnCompleteText(); + + _activeSession.Commit(); + + if (!enterOnComplete) { + return VSConstants.S_OK; + } + } else { + _activeSession.Dismiss(); + } + break; + case VSConstants.VSStd2KCmdID.TYPECHAR: + var ch = (char)(ushort)System.Runtime.InteropServices.Marshal.GetObjectForNativeVariant(pvaIn); + if (_activeSession != null && !_activeSession.IsDismissed) { + if (_activeSession.SelectedCompletionSet.SelectionStatus.IsSelected) { + if (IronPythonToolsPackage.Instance.IntellisenseOptionsPage.CompletionCommittedBy.IndexOf(ch) != -1) { + _activeSession.Commit(); + } + } + } + break; + case VSConstants.VSStd2KCmdID.TAB: + _activeSession.Commit(); + return VSConstants.S_OK; + } + } + } else if (_sigHelpSession != null) { + if (pguidCmdGroup == VSConstants.VSStd2K) { + switch ((VSConstants.VSStd2KCmdID)nCmdID) { + case VSConstants.VSStd2KCmdID.BACKSPACE: + bool fDeleted = Backspace(); + if (fDeleted) { + return VSConstants.S_OK; + } + break; + case VSConstants.VSStd2KCmdID.LEFT: + UpdateCurrentParameter(_textView.Caret.Position.BufferPosition.Position - 1); + break; + case VSConstants.VSStd2KCmdID.RIGHT: + UpdateCurrentParameter(_textView.Caret.Position.BufferPosition.Position + 1); + break; + case VSConstants.VSStd2KCmdID.HOME: + case VSConstants.VSStd2KCmdID.BOL: + case VSConstants.VSStd2KCmdID.BOL_EXT: + case VSConstants.VSStd2KCmdID.END: + case VSConstants.VSStd2KCmdID.WORDPREV: + case VSConstants.VSStd2KCmdID.WORDPREV_EXT: + case VSConstants.VSStd2KCmdID.DELETEWORDLEFT: + _sigHelpSession.Dismiss(); + _sigHelpSession = null; + break; + } + } + } + + return _oldTarget.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); + } + + private bool EnterOnCompleteText() { + SnapshotPoint? point = _activeSession.GetTriggerPoint(_textView.TextBuffer.CurrentSnapshot); + if (point.HasValue) { + int chars = _textView.Caret.Position.BufferPosition.Position - point.Value.Position; + var selectionStatus = _activeSession.SelectedCompletionSet.SelectionStatus; + if (chars == selectionStatus.Completion.InsertionText.Length) { + string text = _textView.TextSnapshot.GetText(point.Value.Position, chars); + + if (String.Compare(text, selectionStatus.Completion.InsertionText, true) == 0) { + return true; + } + } + } + + return false; + } + + public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { + return _oldTarget.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); + } + + #endregion + } +} + diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Intellisense/IntellisenseControllerProvider.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Intellisense/IntellisenseControllerProvider.cs new file mode 100644 index 0000000000..77a43cdeb6 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Intellisense/IntellisenseControllerProvider.cs @@ -0,0 +1,55 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.ComponentModel.Composition; +using Microsoft.IronPythonTools.Library; +using Microsoft.IronStudio; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.IncrementalSearch; +using Microsoft.VisualStudio.Text.Operations; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronPythonTools.Intellisense { + [Export(typeof(IIntellisenseControllerProvider)), ContentType(PythonCoreConstants.ContentType), Order] + class IntellisenseControllerProvider : IIntellisenseControllerProvider { + [Import] + internal ICompletionBroker _CompletionBroker = null; // Set via MEF + [Import] + internal IEditorOperationsFactoryService _EditOperationsFactory = null; // Set via MEF + [Import] + internal IVsEditorAdaptersFactoryService _adaptersFactory { get; set; } + [Import] + internal ISignatureHelpBroker _SigBroker = null; // Set via MEF + [Import] + internal IQuickInfoBroker _QuickInfoBroker = null; // Set via MEF + [Import] + internal IPythonAnalyzer _Analyzer = null; // Set via MEF + [Import] + internal IIncrementalSearchFactoryService _IncrementalSearch = null; // Set via MEF + + public IIntellisenseController TryCreateIntellisenseController(ITextView textView, IList subjectBuffers) { + // Only use the analyzer if the view is actually file backed. If it is the REPL window + // we don't use this. + if (textView.GetFilePath() != null) { + _Analyzer.AnalyzeTextView(textView); + } + + return new IntellisenseController(this, subjectBuffers, textView); + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Intellisense/IntellisensePreKeyProcessor.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Intellisense/IntellisensePreKeyProcessor.cs new file mode 100644 index 0000000000..ed26ee9000 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Intellisense/IntellisensePreKeyProcessor.cs @@ -0,0 +1,94 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Windows.Input; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.IronPythonTools.Intellisense { + internal class IntellisensePreKeyProcessor : KeyProcessor { + private ITextView _wpfTextView; + + private event KeyEventHandler _preProcessKeyDownEvent; + private event KeyEventHandler _preProcessKeyUpEvent; + + public IntellisensePreKeyProcessor(IWpfTextView wpfTextView) { + _wpfTextView = wpfTextView; + _wpfTextView.Properties.GetProperty(typeof(IntellisenseController)).Attach(this); + } + + public override bool IsInterestedInHandledEvents { + get { + return true; + } + } + + public override void KeyDown(KeyEventArgs args) { + this.FireKeyDown(args); + base.KeyDown(args); + } + + public override void KeyUp(KeyEventArgs args) { + this.FireKeyUp(args); + base.KeyUp(args); + } + + public override void TextInput(TextCompositionEventArgs args) { + this.FireTextInput(args); + base.TextInput(args); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + #region Public Surface + + internal event KeyEventHandler PreprocessKeyDown { + add { _preProcessKeyDownEvent += value; } + remove { _preProcessKeyDownEvent -= value; } + } + + internal event KeyEventHandler PreprocessKeyUp { + add { _preProcessKeyUpEvent += value; } + remove { _preProcessKeyUpEvent -= value; } + } + + internal event TextCompositionEventHandler PreprocessTextInput; + + #endregion + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + #region Private Implementation + + private void FireKeyDown(KeyEventArgs args) { + KeyEventHandler handler = _preProcessKeyDownEvent; + if (handler != null) { + handler(_wpfTextView, args); + } + } + + private void FireKeyUp(KeyEventArgs args) { + KeyEventHandler handler = _preProcessKeyUpEvent; + if (handler != null) { + handler(_wpfTextView, args); + } + } + + private void FireTextInput(TextCompositionEventArgs args) { + TextCompositionEventHandler handler = PreprocessTextInput; + if (handler != null) { + handler(_wpfTextView, args); + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Intellisense/IntellisensePreKeyProcessorProvider.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Intellisense/IntellisensePreKeyProcessorProvider.cs new file mode 100644 index 0000000000..5ab10d005a --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Intellisense/IntellisensePreKeyProcessorProvider.cs @@ -0,0 +1,31 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel.Composition; +using Microsoft.IronStudio; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronPythonTools.Intellisense { + [Export(typeof(IKeyProcessorProvider))] + [Name("Intellisense Sample Preprocess KeyProcessor")] + [Order(After = "DefaultKeyProcessor")] + [ContentType(PythonCoreConstants.ContentType)] + [TextViewRole(PredefinedTextViewRoles.Editable)] + internal sealed class IntellisensePreKeyProcessorProvider : IKeyProcessorProvider { + public KeyProcessor GetAssociatedProcessor(IWpfTextView wpfTextView) { + return new IntellisensePreKeyProcessor(wpfTextView); + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/CodeWindowManager.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/CodeWindowManager.cs new file mode 100644 index 0000000000..40a90e9847 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/CodeWindowManager.cs @@ -0,0 +1,119 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.IronPythonTools.Intellisense; +using Microsoft.IronPythonTools.Language; +using Microsoft.PyAnalysis; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Operations; +using Microsoft.VisualStudio.TextManager.Interop; + +namespace Microsoft.IronPythonTools.Navigation { + class CodeWindowManager : IVsCodeWindowManager { + private readonly IVsCodeWindow _window; + private readonly IWpfTextView _textView; + private readonly IEditorOperationsFactoryService _editorOperationsFactory; + private readonly IPythonAnalyzer _analyzer; + private static readonly Dictionary _windows = new Dictionary(); + private DropDownBarClient _client; + + public CodeWindowManager(IVsCodeWindow codeWindow, IWpfTextView textView, IComponentModel componentModel) { + _window = codeWindow; + _textView = textView; + _editorOperationsFactory = componentModel.GetService(); + _analyzer = componentModel.GetService(); + } + + #region IVsCodeWindowManager Members + + public int AddAdornments() { + _windows[_textView] = this; + + IVsTextView textView; + + if (ErrorHandler.Succeeded(_window.GetPrimaryView(out textView))) { + OnNewView(textView); + } + + if (ErrorHandler.Succeeded(_window.GetSecondaryView(out textView))) { + OnNewView(textView); + } + + if (IronPythonToolsPackage.Instance.LangPrefs.NavigationBar) { + return AddDropDownBar(); + } + + return VSConstants.S_OK; + } + + private int AddDropDownBar() { + var pythonProjectEntry = _textView.TextBuffer.GetAnalysis() as IPythonProjectEntry; + if (pythonProjectEntry == null) { + return VSConstants.E_FAIL; + } + + DropDownBarClient dropDown = _client = new DropDownBarClient(_textView, pythonProjectEntry); + IVsDropdownBarManager manager = (IVsDropdownBarManager)_window; + + IVsDropdownBar dropDownBar; + int hr = manager.GetDropdownBar(out dropDownBar); + if (ErrorHandler.Succeeded(hr) && dropDownBar != null) { + hr = manager.RemoveDropdownBar(); + if (!ErrorHandler.Succeeded(hr)) { + return hr; + } + } + + return manager.AddDropdownBar(2, dropDown); + } + + private int RemoveDropDownBar() { + if (_client != null) { + IVsDropdownBarManager manager = (IVsDropdownBarManager)_window; + _client.Unregister(); + _client = null; + return manager.RemoveDropdownBar(); + } + return VSConstants.S_OK; + } + + public int OnNewView(IVsTextView pView) { + // TODO: We pass _textView which may not be right for split buffers, we need + // to test the case where we split a text file and save it as an existing file? + new EditFilter(_analyzer, _textView, pView); + return VSConstants.S_OK; + } + + public int RemoveAdornments() { + _windows.Remove(_textView); + return RemoveDropDownBar(); + } + + public static void ToggleNavigationBar(bool fEnable) { + foreach (var keyValue in _windows) { + if (fEnable) { + ErrorHandler.ThrowOnFailure(keyValue.Value.AddDropDownBar()); + } else { + ErrorHandler.ThrowOnFailure(keyValue.Value.RemoveDropDownBar()); + } + } + } + + #endregion + } + +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/DropDownBarClient.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/DropDownBarClient.cs new file mode 100644 index 0000000000..781e6fe9c9 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/DropDownBarClient.cs @@ -0,0 +1,649 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Windows.Forms; +using System.Windows.Threading; +using IronPython.Compiler.Ast; +using Microsoft.IronPythonTools.Intellisense; +using Microsoft.PyAnalysis; +using Microsoft.Scripting; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.TextManager.Interop; + +namespace Microsoft.IronPythonTools.Navigation { + /// + /// Implements the navigation bar which appears above a source file in the editor. + /// + /// The navigation bar consists of two drop-down boxes. On the left hand side is a list + /// of top level constructs. On the right hand side are list of nested constructs for the + /// currently selected top-level construct. + /// + /// When the user moves the caret the current selections are automatically updated. If the + /// user is inside of a top level construct but not inside any of the available nested + /// constructs then the first element of the nested construct list is selected and displayed + /// grayed out. If the user is inside of no top level constructs then the 1st top-level + /// construct is selected and displayed as grayed out. It's first top-level construct is + /// also displayed as being grayed out. + /// + /// The most difficult part of this is handling the transitions from one state to another. + /// We need to change the current selections due to events from two sources: The first is selections + /// in the drop down and the 2nd is the user navigating within the source code. When a change + /// occurs we may need to update the left hand side (along w/ a corresponding update to the right + /// hand side) or we may need to update the right hand side. If we are transitioning from + /// being outside of a known element to being in a known element we also need to refresh + /// the drop down to remove grayed out elements. + /// + class DropDownBarClient : IVsDropdownBarClient { + private readonly IPythonProjectEntry _classifier; // classifier which is used for providing entries + private ReadOnlyCollection _topLevelEntries; // entries for top-level members of the file + private ReadOnlyCollection _nestedEntries; // entries for nested members in the file + private readonly Dispatcher _dispatcher; // current dispatcher so we can get back to our thread + private readonly IWpfTextView _textView; // text view we're drop downs for + private IVsDropdownBar _dropDownBar; // drop down bar - used to refresh when changes occur + private int _curTopLevelIndex = -1, _curNestedIndex = -1; // currently selected indices for each bar + + private static readonly ImageList _imageList = GetImageList(); + private static readonly ReadOnlyCollection EmptyEntries = new ReadOnlyCollection(new DropDownEntryInfo[0]); + + private const int TopLevelComboBoxId = 0; + private const int NestedComboBoxId = 1; + + public DropDownBarClient(IWpfTextView textView, IPythonProjectEntry classifier) { + Debug.Assert(textView != null); + Debug.Assert(classifier != null); + + _classifier = classifier; + _classifier.OnNewParseTree += ParserOnNewParseTree; + _textView = textView; + _topLevelEntries = _nestedEntries = EmptyEntries; + _dispatcher = Dispatcher.CurrentDispatcher; + _textView.Caret.PositionChanged += CaretPositionChanged; + } + + internal void Unregister() { + _classifier.OnNewParseTree -= ParserOnNewParseTree; + _textView.Caret.PositionChanged -= CaretPositionChanged; + } + + #region IVsDropdownBarClient Members + + /// + /// Gets the attributes for the specified combo box. We return teh number of elements that we will + /// display, the various attributes that VS should query for next (text, image, and attributes of + /// the text such as being grayed out), along with the appropriate image list. + /// + /// We always return the # of entries based off our entries list, the exact same image list, and + /// we have VS query for text, image, and text attributes all the time. + /// + public int GetComboAttributes(int iCombo, out uint pcEntries, out uint puEntryType, out IntPtr phImageList) { + uint count = 0; + + switch (iCombo) { + case TopLevelComboBoxId: + CalculateTopLevelEntries(); + count = (uint)_topLevelEntries.Count; + break; + case NestedComboBoxId: + CalculateNestedEntries(); + count = (uint)_nestedEntries.Count; + break; + } + + pcEntries = count; + puEntryType = (uint)(DROPDOWNENTRYTYPE.ENTRY_TEXT | DROPDOWNENTRYTYPE.ENTRY_IMAGE | DROPDOWNENTRYTYPE.ENTRY_ATTR); + phImageList = _imageList.Handle; + return VSConstants.S_OK; + } + + public int GetComboTipText(int iCombo, out string pbstrText) { + pbstrText = null; + return VSConstants.S_OK; + } + + /// + /// Gets the entry attributes for the given combo box and index. + /// + /// We always use plain text unless we are not inside of a valid entry + /// for the given combo box. In that case we ensure the 1st item + /// is selected and we gray out the 1st entry. + /// + public int GetEntryAttributes(int iCombo, int iIndex, out uint pAttr) { + pAttr = (uint)DROPDOWNFONTATTR.FONTATTR_PLAIN; + + if (iIndex == 0) { + switch (iCombo) { + case TopLevelComboBoxId: + if (_curTopLevelIndex == -1) { + pAttr = (uint)DROPDOWNFONTATTR.FONTATTR_GRAY; + } + break; + case NestedComboBoxId: + if (_curNestedIndex == -1) { + pAttr = (uint)DROPDOWNFONTATTR.FONTATTR_GRAY; + } + break; + } + } + + return VSConstants.S_OK; + } + + /// + /// Gets the image which is associated with the given index for the + /// given combo box. + /// + public int GetEntryImage(int iCombo, int iIndex, out int piImageIndex) { + piImageIndex = 0; + + switch (iCombo) { + case TopLevelComboBoxId: + var topLevel = _topLevelEntries; + if (iIndex < topLevel.Count) { + piImageIndex = topLevel[iIndex].ImageListIndex; + } + break; + case NestedComboBoxId: + var nested = _nestedEntries; + if (iIndex < nested.Count) { + piImageIndex = nested[iIndex].ImageListIndex; + } + break; + } + + return VSConstants.S_OK; + } + + /// + /// Gets the text which is displayed for the given index for the + /// given combo box. + /// + public int GetEntryText(int iCombo, int iIndex, out string ppszText) { + ppszText = String.Empty; + switch (iCombo) { + case TopLevelComboBoxId: + var topLevel = _topLevelEntries; + if (iIndex < topLevel.Count) { + ppszText = topLevel[iIndex].Name; + } + break; + case NestedComboBoxId: + var nested = _nestedEntries; + if (iIndex < nested.Count) { + ppszText = nested[iIndex].Name; + } + break; + } + + return VSConstants.S_OK; + } + + public int OnComboGetFocus(int iCombo) { + return VSConstants.S_OK; + } + + /// + /// Called when the user selects an item from the drop down. We will + /// update the caret to beat the correct location, move the view port + /// so that the code is centered on the screen, and we may refresh + /// the combo box so that the 1st item is no longer grayed out if + /// the user was originally outside of valid selection. + /// + public int OnItemChosen(int iCombo, int iIndex) { + switch (iCombo) { + case TopLevelComboBoxId: + _dropDownBar.RefreshCombo(NestedComboBoxId, 0); + var topLevel = _topLevelEntries; + if (iIndex < topLevel.Count) { + int oldIndex = _curTopLevelIndex; + _curTopLevelIndex = iIndex; + if (oldIndex == -1) { + _dropDownBar.RefreshCombo(TopLevelComboBoxId, iIndex); + } + CenterAndFocus(topLevel[iIndex].Start.Index); + } + break; + case NestedComboBoxId: + var nested = _nestedEntries; + if (iIndex < nested.Count) { + int oldIndex = _curNestedIndex; + _curNestedIndex = iIndex; + if (oldIndex == -1) { + _dropDownBar.RefreshCombo(NestedComboBoxId, iIndex); + } + CenterAndFocus(nested[iIndex].Start.Index); + } + break; + } + return VSConstants.S_OK; + } + + public int OnItemSelected(int iCombo, int iIndex) { + return VSConstants.S_OK; + } + + /// + /// Called by VS to provide us with the drop down bar. We can call back + /// on the drop down bar to force VS to refresh the combo box or change + /// the current selection. + /// + public int SetDropdownBar(IVsDropdownBar pDropdownBar) { + _dropDownBar = pDropdownBar; + return VSConstants.S_OK; + } + + #endregion + + #region Selection Synchronization + + private void CaretPositionChanged(object sender, CaretPositionChangedEventArgs e) { + int newPosition = e.NewPosition.BufferPosition.Position; + + var topLevel = _topLevelEntries; + int curTopLevel = _curTopLevelIndex; + + if (curTopLevel != -1 && curTopLevel < topLevel.Count) { + if (newPosition >= topLevel[curTopLevel].Start.Index && newPosition <= topLevel[curTopLevel].End.Index) { + UpdateNestedComboSelection(newPosition); + } else { + FindActiveTopLevelComboSelection(newPosition, topLevel); + } + } else { + FindActiveTopLevelComboSelection(newPosition, topLevel); + } + } + + private void FindActiveTopLevelComboSelection(int newPosition, ReadOnlyCollection topLevel) { + int oldTopLevel = _curTopLevelIndex; + + // left side has changed + bool found = false; + for (int i = 0; i < topLevel.Count; i++) { + if (newPosition >= topLevel[i].Start.Index && newPosition <= topLevel[i].End.Index) { + _curTopLevelIndex = i; + + // we found a new left hand side + if (oldTopLevel == -1) { + // we've selected something new, we need to refresh the combo to + // to remove the grayed out entry + _dropDownBar.RefreshCombo(TopLevelComboBoxId, i); + } else { + // changing from one top-level to another, just update the selection + _dropDownBar.SetCurrentSelection(TopLevelComboBoxId, i); + } + + // update the nested entries + CalculateNestedEntries(); + _dropDownBar.RefreshCombo(NestedComboBoxId, 0); + UpdateNestedComboSelection(newPosition); + found = true; + break; + } + } + + if (!found) { + // there's no associated entry, we should disable the bar + _curTopLevelIndex = -1; + _curNestedIndex = -1; + + if (oldTopLevel != -1) { + // we need to refresh to make the entry gray and + _dropDownBar.RefreshCombo(TopLevelComboBoxId, 0); + } + } + + UpdateNestedComboSelection(newPosition); + } + + private void UpdateNestedComboSelection(int newPosition) { + // left side has not changed, check rhs + int curNested = _curNestedIndex; + var nested = _nestedEntries; + + if (curNested != -1 && curNested < nested.Count) { + if (newPosition < nested[curNested].Start.Index || newPosition > nested[curNested].End.Index) { + // right hand side has changed + FindActiveNestedSelection(newPosition, nested); + } + } else { + FindActiveNestedSelection(newPosition, nested); + } + } + + private void FindActiveNestedSelection(int newPosition, ReadOnlyCollection nested) { + int oldNested = _curNestedIndex; + + bool found = false; + + if (_curTopLevelIndex != -1) { + for (int i = 0; i < nested.Count; i++) { + if (newPosition >= nested[i].Start.Index && newPosition <= nested[i].End.Index) { + _curNestedIndex = i; + + if (oldNested == -1) { + // we've selected something new, we need to refresh the combo to + // to remove the grayed out entry + _dropDownBar.RefreshCombo(NestedComboBoxId, i); + } else { + // changing from one nested to another, just update the selection + _dropDownBar.SetCurrentSelection(NestedComboBoxId, i); + } + + found = true; + break; + } + } + } + + if (!found) { + // there's no associated entry, we should disable the bar + _curNestedIndex = -1; + + // we need to refresh to make the entry gray and + _dropDownBar.RefreshCombo(NestedComboBoxId, 0); + } + } + + #endregion + + #region Entry Calculation + + /// + /// Data structure used for tracking elements of the drop down box in the navigation bar. + /// + struct DropDownEntryInfo { + public readonly Statement Body; + + public DropDownEntryInfo(Statement body) { + Body = body; + } + + /// + /// Gets the name which should be displayed for the text in the drop down. + /// + public string Name { + get { + ClassDefinition klass = Body as ClassDefinition; + if (klass != null) { + return klass.Name; + } + + FunctionDefinition func = Body as FunctionDefinition; + if (func != null) { + return func.Name; + } + + return String.Empty; + } + } + + /// + /// Gets the index in our image list which should be used for the icon to display + /// next to the drop down element. + /// + public int ImageListIndex { + get { + ImageListOverlay overlay = ImageListOverlay.ImageListOverlayNone; + string name = Name; + if (name != null && name.StartsWith("_") && !(name.StartsWith("__") && name.EndsWith("__"))) { + overlay = ImageListOverlay.ImageListOverlayPrivate; + } + + FunctionDefinition funcDef; + if (Body is ClassDefinition) { + return GetImageListIndex(ImageListKind.Class, overlay); + } else if ((funcDef = Body as FunctionDefinition) != null) { + return GetImageListIndex(GetImageListKind(funcDef), overlay); + } + + return 0; + } + } + + private static ImageListKind GetImageListKind(FunctionDefinition funcDef) { + ImageListKind imageKind = ImageListKind.Method; + if (funcDef.Decorators != null && funcDef.Decorators.Count == 1) { + foreach (var decorator in funcDef.Decorators) { + NameExpression nameExpr = decorator as NameExpression; + if (nameExpr != null) { + if (nameExpr.Name == "property") { + imageKind = ImageListKind.Property; + break; + } else if (nameExpr.Name == "staticmethod") { + imageKind = ImageListKind.StaticMethod; + break; + } else if (nameExpr.Name == "classmethod") { + imageKind = ImageListKind.ClassMethod; + break; + } + } + } + } + return imageKind; + } + + /// + /// Gets the location where the language element associated with the drop + /// down entry begins. + /// + public SourceLocation Start { + get { + ClassDefinition klass = Body as ClassDefinition; + if (klass != null) { + return klass.Start; + } + + FunctionDefinition func = Body as FunctionDefinition; + if (func != null) { + return func.Start; + } + + return SourceLocation.None; + } + } + + /// + /// Gets the location where the language element associated with the + /// drop down ends. + /// + public SourceLocation End { + get { + ClassDefinition klass = Body as ClassDefinition; + if (klass != null) { + return klass.End; + } + + FunctionDefinition func = Body as FunctionDefinition; + if (func != null) { + return func.End; + } + + return SourceLocation.None; + } + } + } + + /// + /// An enum which is synchronized with our image list for the various + /// kinds of images which are available. This can be combined with the + /// ImageListOverlay to select an image for the appropriate member type + /// and indicate the appropiate visiblity. These can be combined with + /// GetImageListIndex to get the final index. + /// + /// Most of these are unused as we're just using an image list shipped + /// by the VS SDK. + /// + enum ImageListKind { + Class, + Unknown1, + Unknown2, + Enum, + Unknown3, + Lightning, + Unknown4, + BlueBox, + Key, + BlueStripe, + ThreeDashes, + TwoBoxes, + Method, + StaticMethod, + Unknown6, + Namespace, + Unknown7, + Property, + Unknown8, + Unknown9, + Unknown10, + Unknown11, + Unknown12, + Unknown13, + ClassMethod + } + + /// + /// Indicates the overlay kind which should be used for a drop down members + /// image. The overlay kind typically indicates visibility. + /// + /// Most of these are unused as we're just using an image list shipped + /// by the VS SDK. + /// + enum ImageListOverlay { + ImageListOverlayNone, + ImageListOverlayLetter, + ImageListOverlayBlue, + ImageListOverlayKey, + ImageListOverlayPrivate, + ImageListOverlayArrow, + } + + /// + /// Turns an image list kind / overlay into the proper index in the image list. + /// + private static int GetImageListIndex(ImageListKind kind, ImageListOverlay overlay) { + return ((int)kind) * 6 + (int)overlay; + } + + /// + /// Reads our image list from our DLLs resource stream. + /// + private static ImageList GetImageList() { + ImageList list = new ImageList(); + list.ImageSize = new Size(0x10, 0x10); + list.TransparentColor = Color.FromArgb(0xff, 0, 0xff); + Stream manifestResourceStream = typeof(Microsoft.IronStudio.Repl.VsReplWindow).Assembly.GetManifestResourceStream("Microsoft.Resources.completionset.bmp"); + list.Images.AddStrip(new Bitmap(manifestResourceStream)); + return list; + } + + /// + /// Helper function for calculating all of the drop down entries that are available + /// in the given suite statement. Called to calculate both the members of top-level + /// code and class bodies. + /// + private static ReadOnlyCollection CalculateEntries(SuiteStatement suite) { + List newEntries = new List(); + + if (suite != null) { + foreach (Statement stmt in suite.Statements) { + if (stmt is ClassDefinition || stmt is FunctionDefinition) { + newEntries.Add(new DropDownEntryInfo(stmt)); + } + } + } + + newEntries.Sort(ComparisonFunction); + return new ReadOnlyCollection(newEntries); + } + + private static int ComparisonFunction(DropDownEntryInfo x, DropDownEntryInfo y) { + string xName = x.Name ?? String.Empty, yName = y.Name ?? String.Empty; + + return NormalCompletionAnalysis.MemberSortComparison(xName, yName); + } + + /// + /// Calculates the members of the drop down for top-level members. + /// + private void CalculateTopLevelEntries() { + PythonAst ast = _classifier.Tree; + if (ast != null) { + _topLevelEntries = CalculateEntries(ast.Body as SuiteStatement); + } + } + + /// + /// Calculates the members of the drop down for nested members + /// based upon the currently selected top-level member. + /// + private void CalculateNestedEntries() { + var entries = _topLevelEntries; + int topLevelIndex = _curTopLevelIndex; + if (entries.Count == 0) { + _nestedEntries = EmptyEntries; + } else if (topLevelIndex < entries.Count) { + var info = entries[topLevelIndex == -1 ? 0 : topLevelIndex]; + + ClassDefinition klass = info.Body as ClassDefinition; + if (klass != null) { + _nestedEntries = CalculateEntries(klass.Body as SuiteStatement); + } else { + _nestedEntries = EmptyEntries; + } + } + } + + #endregion + + #region Implementation Details + + /// + /// Wired to parser event for when the parser has completed parsing a new tree and we need + /// to update the navigation bar with the new data. + /// + private void ParserOnNewParseTree(object sender, EventArgs e) { + var dropDownBar = _dropDownBar; + if (dropDownBar != null) { + Action callback = () => { dropDownBar.RefreshCombo(0, 0); }; + _dispatcher.BeginInvoke(callback, DispatcherPriority.Background); + } + } + + /// + /// Moves the caret to the specified index in the current snapshot. Then updates the view port + /// so that caret will be centered. Finally moves focus to the text view so the user can + /// continue typing. + /// + private void CenterAndFocus(int index) { + _textView.Caret.MoveTo(new SnapshotPoint(_textView.TextBuffer.CurrentSnapshot, index)); + + _textView.ViewScroller.EnsureSpanVisible( + new SnapshotSpan(_textView.TextBuffer.CurrentSnapshot, index, 1), + EnsureSpanVisibleOptions.AlwaysCenter + ); + + ((System.Windows.Controls.Control)_textView).Focus(); + } + + #endregion + + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/EditFilter.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/EditFilter.cs new file mode 100644 index 0000000000..1daea3965a --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/EditFilter.cs @@ -0,0 +1,687 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Windows; +using Microsoft.IronPythonTools.Commands; +using Microsoft.IronPythonTools.Editor.Core; +using Microsoft.IronPythonTools.Intellisense; +using Microsoft.IronPythonTools.Navigation; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Core.Repl; +using Microsoft.IronStudio.Navigation; +using Microsoft.PyAnalysis; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods; +using Microsoft.VisualStudio.TextManager.Interop; + +namespace Microsoft.IronPythonTools.Language { + /// + /// IOleCommandTarget implementation for interacting with various editor commands. This enables + /// wiring up most of our features to the VisualStudio editor. We currently support: + /// Goto Definition + /// Find All References + /// Show Member List + /// Complete Word + /// Enable/Disable Outlining + /// Comment/Uncomment block + /// + /// We also support IronPython specific commands via this class. Currently these commands are + /// added by updating our CommandTable class to contain a new command. These commands also need + /// to be registered in our .vsct file so that VS knows about them. + /// + class EditFilter : IOleCommandTarget { + private readonly IWpfTextView _textView; + private readonly IOleCommandTarget _next; + private readonly IPythonAnalyzer _analyzer; + + public EditFilter(IPythonAnalyzer analyzer, IWpfTextView textView, IVsTextView vsTextView) { + _textView = textView; + _analyzer = analyzer; + ErrorHandler.ThrowOnFailure(vsTextView.AddCommandFilter(this, out _next)); + } + + /// + /// Implements Goto Definition. Called when the user selects Goto Definition from the + /// context menu or hits the hotkey associated with Goto Definition. + /// + /// If there is 1 and only one definition immediately navigates to it. If there are + /// no references displays a dialog box to the user. Otherwise it opens the find + /// symbols dialog with the list of results. + /// + private int GotoDefinition() { + UpdateStatusForIncompleteAnalysis(); + + var analysis = GetExpressionAnalysis(); + + Dictionary references, definitions, values; + GetDefsRefsAndValues(analysis, out definitions, out references, out values); + + if ((values.Count + definitions.Count) == 1) { + if (values.Count != 0) { + foreach (var location in values.Keys) { + location.GotoSource(); + break; + } + } else { + foreach (var location in definitions.Keys) { + location.GotoSource(); + break; + } + } + } else if (values.Count + definitions.Count == 0) { + if (references.Count != 0) { + ShowFindSymbolsDialog(analysis, new SymbolList("References", StandardGlyphGroup.GlyphReference, references.Values)); + } else { + MessageBox.Show(String.Format("Cannot go to definition \"{0}\"", analysis.Expression)); + } + } else if (definitions.Count == 0) { + ShowFindSymbolsDialog(analysis, new SymbolList("Values", StandardGlyphGroup.GlyphForwardType, values.Values)); + } else if (values.Count == 0) { + ShowFindSymbolsDialog(analysis, new SymbolList("Definitions", StandardGlyphGroup.GlyphLibrary, definitions.Values)); + } else { + ShowFindSymbolsDialog(analysis, + new SymbolList("Definitions", StandardGlyphGroup.GlyphLibrary, definitions.Values), + new SymbolList("Values", StandardGlyphGroup.GlyphForwardType, values.Values) + ); + } + + return VSConstants.S_OK; + } + + /// + /// Implements Find All References. Called when the user selects Find All References from + /// the context menu or hits the hotkey associated with find all references. + /// + /// Always opens the Find Symbol Results box to display the results. + /// + private int FindAllReferences() { + UpdateStatusForIncompleteAnalysis(); + + var analysis = GetExpressionAnalysis(); + + Dictionary references, definitions, values; + GetDefsRefsAndValues(analysis, out definitions, out references, out values); + + ShowFindSymbolsDialog(analysis, + new SymbolList("Definitions", StandardGlyphGroup.GlyphLibrary, definitions.Values), + new SymbolList("Values", StandardGlyphGroup.GlyphForwardType, values.Values), + new SymbolList("References", StandardGlyphGroup.GlyphReference, references.Values) + ); + + return VSConstants.S_OK; + } + + private static void GetDefsRefsAndValues(ExpressionAnalysis provider, out Dictionary definitions, out Dictionary references, out Dictionary values) { + references = new Dictionary(); + definitions = new Dictionary(); + values = new Dictionary(); + + foreach (var v in provider.Variables) { + switch (v.Type) { + case VariableType.Definition: + values.Remove(v.Location); + definitions[v.Location] = new SimpleLocationInfo(provider.Expression, v.Location, StandardGlyphGroup.GlyphGroupField); + break; + case VariableType.Reference: + references[v.Location] = new SimpleLocationInfo(provider.Expression, v.Location, StandardGlyphGroup.GlyphGroupField); + break; + case VariableType.Value: + if (!definitions.ContainsKey(v.Location)) { + values[v.Location] = new SimpleLocationInfo(provider.Expression, v.Location, StandardGlyphGroup.GlyphGroupField); + } + break; + } + } + } + + /// + /// Opens the find symbols dialog with a list of results. This is done by requesting + /// that VS does a search against our library GUID. Our library then responds to + /// that request by extracting the prvoided symbol list out and using that for the + /// search results. + /// + private static void ShowFindSymbolsDialog(ExpressionAnalysis provider, params SymbolList[] symbols) { + // ensure our library is loaded so find all references will go to our library + Package.GetGlobalService(typeof(IPythonLibraryManager)); + + if (provider.Expression != "") { + var findSym = (IVsFindSymbol)IronPythonToolsPackage.GetGlobalService(typeof(SVsObjectSearch)); + VSOBSEARCHCRITERIA2 searchCriteria = new VSOBSEARCHCRITERIA2(); + searchCriteria.eSrchType = VSOBSEARCHTYPE.SO_ENTIREWORD; + searchCriteria.pIVsNavInfo = symbols.Length == 1 ? (IVsNavInfo)symbols[0] : (IVsNavInfo)new LocationCategory("Test", symbols); + searchCriteria.grfOptions = (uint)_VSOBSEARCHOPTIONS2.VSOBSO_LISTREFERENCES; + searchCriteria.szName = provider.Expression; + + Guid guid = Guid.Empty; + // new Guid("{a5a527ea-cf0a-4abf-b501-eafe6b3ba5c6}") + ErrorHandler.ThrowOnFailure(findSym.DoSearch(new Guid(CommonConstants.LibraryGuid), new VSOBSEARCHCRITERIA2[] { searchCriteria })); + } else { + var statusBar = (IVsStatusbar)CommonPackage.GetGlobalService(typeof(SVsStatusbar)); + statusBar.SetText("The caret must be on valid expression to find all references."); + } + } + + private ExpressionAnalysis GetExpressionAnalysis() { + var textView = _textView; + var textBuffer = _textView.TextBuffer; + var snapshot = textBuffer.CurrentSnapshot; + int caretPos = _textView.Caret.Position.BufferPosition.Position; + + // foo( + // ^ + // +--- Caret here + // + // We want to lookup foo, not foo( + // + ITrackingSpan span; + if (caretPos != snapshot.Length) { + string curChar = snapshot.GetText(caretPos, 1); + if (!IsIdentifierChar(curChar[0]) && caretPos > 0) { + string prevChar = snapshot.GetText(caretPos - 1, 1); + if (IsIdentifierChar(prevChar[0])) { + caretPos--; + } + } + span = snapshot.CreateTrackingSpan( + caretPos, + 1, + SpanTrackingMode.EdgeInclusive + ); + } else { + span = snapshot.CreateTrackingSpan( + caretPos, + 0, + SpanTrackingMode.EdgeInclusive + ); + } + + return _analyzer.AnalyzeExpression(snapshot, textBuffer, span); + } + + class LocationCategory : SimpleObjectList, IVsNavInfo, ICustomSearchListProvider { + private readonly string _name; + + public LocationCategory(string name, params SymbolList[] locations) { + _name = name; + + foreach (var location in locations) { + if (location.Children.Count > 0) { + Children.Add(location); + } + } + } + + public override uint CategoryField(LIB_CATEGORY lIB_CATEGORY) { + return (uint)(_LIB_LISTTYPE.LLT_HIERARCHY | _LIB_LISTTYPE.LLT_MEMBERS | _LIB_LISTTYPE.LLT_PACKAGE); + } + + #region IVsNavInfo Members + + public int EnumCanonicalNodes(out IVsEnumNavInfoNodes ppEnum) { + ppEnum = new NodeEnumerator(Children); + return VSConstants.S_OK; + } + + public int EnumPresentationNodes(uint dwFlags, out IVsEnumNavInfoNodes ppEnum) { + ppEnum = new NodeEnumerator(Children); + return VSConstants.S_OK; + } + + public int GetLibGuid(out Guid pGuid) { + pGuid = Guid.Empty; + return VSConstants.S_OK; + } + + public int GetSymbolType(out uint pdwType) { + pdwType = (uint)_LIB_LISTTYPE2.LLT_MEMBERHIERARCHY; + return VSConstants.S_OK; + } + + #endregion + + #region ICustomSearchListProvider Members + + public IVsSimpleObjectList2 GetSearchList() { + return this; + } + + #endregion + } + + class SimpleLocationInfo : SimpleObject, IVsNavInfoNode { + private readonly LocationInfo _locationInfo; + private readonly StandardGlyphGroup _glyphType; + private readonly string _pathText, _lineText; + + public SimpleLocationInfo(string searchText, LocationInfo locInfo, StandardGlyphGroup glyphType) { + _locationInfo = locInfo; + _glyphType = glyphType; + _pathText = GetSearchDisplayText(); + _lineText = _locationInfo.ProjectEntry.GetLine(_locationInfo.Line); + } + + public override string Name { + get { + return _locationInfo.FilePath; + } + } + + public override string GetTextRepresentation(VSTREETEXTOPTIONS options) { + if (options == VSTREETEXTOPTIONS.TTO_DEFAULT) { + return _pathText + _lineText.Trim(); + } + return String.Empty; + } + + private string GetSearchDisplayText() { + return String.Format("{0} - ({1}, {2}): ", + _locationInfo.FilePath, + _locationInfo.Line, + _locationInfo.Column); + } + + public override string UniqueName { + get { + return _locationInfo.FilePath; + } + } + + public override bool CanGoToSource { + get { + return true; + } + } + + public override VSTREEDISPLAYDATA DisplayData { + get { + var res = new VSTREEDISPLAYDATA(); + res.Image = res.SelectedImage = (ushort)_glyphType; + res.State = (uint)_VSTREEDISPLAYSTATE.TDS_FORCESELECT; + + // This code highlights the text but it gets the wrong region. This should be re-enabled + // and highlight the correct region. + + //res.ForceSelectStart = (ushort)(_pathText.Length + _locationInfo.Column - 1); + //res.ForceSelectLength = (ushort)_locationInfo.Length; + return res; + } + } + + public override void GotoSource(VSOBJGOTOSRCTYPE SrcType) { + _locationInfo.GotoSource(); + } + + #region IVsNavInfoNode Members + + public int get_Name(out string pbstrName) { + pbstrName = _locationInfo.FilePath; + return VSConstants.S_OK; + } + + public int get_Type(out uint pllt) { + pllt = 16; // (uint)_LIB_LISTTYPE2.LLT_MEMBERHIERARCHY; + return VSConstants.S_OK; + } + + #endregion + } + + class SymbolList : SimpleObjectList, IVsNavInfo, IVsNavInfoNode, ICustomSearchListProvider, ISimpleObject { + private readonly string _name; + private readonly StandardGlyphGroup _glyphGroup; + + public SymbolList(string description, StandardGlyphGroup glyphGroup, IEnumerable locations) { + _name = description; + _glyphGroup = glyphGroup; + Children.AddRange(locations); + } + + public override uint CategoryField(LIB_CATEGORY lIB_CATEGORY) { + return (uint)(_LIB_LISTTYPE.LLT_MEMBERS | _LIB_LISTTYPE.LLT_PACKAGE); + } + + #region ISimpleObject Members + + public bool CanDelete { + get { return false; } + } + + public bool CanGoToSource { + get { return false; } + } + + public bool CanRename { + get { return false; } + } + + public string Name { + get { return _name; } + } + + public string UniqueName { + get { return _name; } + } + + public string GetTextRepresentation(VSTREETEXTOPTIONS options) { + switch(options) { + case VSTREETEXTOPTIONS.TTO_DISPLAYTEXT: + return _name; + } + return null; + } + + public string TooltipText { + get { return null; } + } + + public object BrowseObject { + get { return null; } + } + + public System.ComponentModel.Design.CommandID ContextMenuID { + get { return null; } + } + + public VSTREEDISPLAYDATA DisplayData { + get { + var res = new VSTREEDISPLAYDATA(); + res.Image = res.SelectedImage = (ushort)_glyphGroup; + return res; + } + } + + public void Delete() { + } + + public void DoDragDrop(OleDataObject dataObject, uint grfKeyState, uint pdwEffect) { + } + + public void Rename(string pszNewName, uint grfFlags) { + } + + public void GotoSource(VSOBJGOTOSRCTYPE SrcType) { + } + + public void SourceItems(out IVsHierarchy ppHier, out uint pItemid, out uint pcItems) { + ppHier = null; + pItemid = 0; + pcItems = 0; + } + + public uint EnumClipboardFormats(_VSOBJCFFLAGS _VSOBJCFFLAGS, VSOBJCLIPFORMAT[] rgcfFormats) { + return VSConstants.S_OK; + } + + public void FillDescription(_VSOBJDESCOPTIONS _VSOBJDESCOPTIONS, IVsObjectBrowserDescription3 pobDesc) { + } + + public IVsSimpleObjectList2 FilterView(uint ListType) { + return this; + } + + #endregion + + #region IVsNavInfo Members + + public int EnumCanonicalNodes(out IVsEnumNavInfoNodes ppEnum) { + ppEnum = new NodeEnumerator(Children); + return VSConstants.S_OK; + } + + public int EnumPresentationNodes(uint dwFlags, out IVsEnumNavInfoNodes ppEnum) { + ppEnum = new NodeEnumerator(Children); + return VSConstants.S_OK; + } + + public int GetLibGuid(out Guid pGuid) { + pGuid = Guid.Empty; + return VSConstants.S_OK; + } + + public int GetSymbolType(out uint pdwType) { + pdwType = (uint)_LIB_LISTTYPE2.LLT_MEMBERHIERARCHY; + return VSConstants.S_OK; + } + + #endregion + + #region ICustomSearchListProvider Members + + public IVsSimpleObjectList2 GetSearchList() { + return this; + } + + #endregion + + #region IVsNavInfoNode Members + + public int get_Name(out string pbstrName) { + pbstrName = "name"; + return VSConstants.S_OK; + } + + public int get_Type(out uint pllt) { + pllt = 16; // (uint)_LIB_LISTTYPE2.LLT_MEMBERHIERARCHY; + return VSConstants.S_OK; + } + + #endregion + } + + class NodeEnumerator : IVsEnumNavInfoNodes where T : IVsNavInfoNode { + private readonly List _locations; + private IEnumerator _locationEnum; + + public NodeEnumerator(List locations) { + _locations = locations; + Reset(); + } + + #region IVsEnumNavInfoNodes Members + + public int Clone(out IVsEnumNavInfoNodes ppEnum) { + ppEnum = new NodeEnumerator(_locations); + return VSConstants.S_OK; + } + + public int Next(uint celt, IVsNavInfoNode[] rgelt, out uint pceltFetched) { + pceltFetched = 0; + while (celt-- != 0 && _locationEnum.MoveNext()) { + rgelt[pceltFetched++] = _locationEnum.Current; + } + return VSConstants.S_OK; + } + + public int Reset() { + _locationEnum = _locations.GetEnumerator(); + return VSConstants.S_OK; + } + + public int Skip(uint celt) { + while (celt-- != 0) { + _locationEnum.MoveNext(); + } + return VSConstants.S_OK; + } + + #endregion + } + + private static bool IsIdentifierChar(char curChar) { + return Char.IsLetterOrDigit(curChar) || curChar == '_'; + } + + private void UpdateStatusForIncompleteAnalysis() { + var statusBar = (IVsStatusbar)CommonPackage.GetGlobalService(typeof(SVsStatusbar)); + if (!IronPythonToolsPackage.Instance.Analyzer.IsAnalyzing) { + statusBar.SetText("Python source analysis is not up to date"); + } + } + + #region IOleCommandTarget Members + + /// + /// Called from VS when we should handle a command or pass it on. + /// + public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { + // preprocessing + if (pguidCmdGroup == VSConstants.GUID_VSStandardCommandSet97) { + switch ((VSConstants.VSStd97CmdID)nCmdID) { + case VSConstants.VSStd97CmdID.GotoDefn: return GotoDefinition(); + case VSConstants.VSStd97CmdID.FindReferences: return FindAllReferences(); + + } + } else if (pguidCmdGroup == CommonConstants.Std2KCmdGroupGuid) { + OutliningTaggerProvider.OutliningTagger tagger; + switch ((VSConstants.VSStd2KCmdID)nCmdID) { + case VSConstants.VSStd2KCmdID.RETURN: + if (IronPythonToolsPackage.Instance.LangPrefs.IndentMode == vsIndentStyle.vsIndentStyleSmart) { + // smart indent + + AutoIndent.HandleReturn(_textView, (IClassifier)_textView.TextBuffer.Properties.GetProperty(typeof(IDlrClassifier))); + return VSConstants.S_OK; + } + break; + case VSConstants.VSStd2KCmdID.BACKSPACE: + + if (IronPythonToolsPackage.Instance.LangPrefs.IndentMode == vsIndentStyle.vsIndentStyleSmart && + _textView.Selection.IsEmpty) { + int indentSize = _textView.Options.GetIndentSize(); + // smart dedent + var containingLine = _textView.Caret.Position.BufferPosition.GetContainingLine(); + var curLineLine = containingLine.GetText(); + + int lineOffset = _textView.Caret.Position.BufferPosition.Position - containingLine.Start.Position; + if (lineOffset >= indentSize) { + bool allSpaces = true; + for (int i = lineOffset - 1; i >= lineOffset - indentSize; i--) { + if (curLineLine[i] != ' ') { + allSpaces = false; + break; + } + } + + if (allSpaces) { + _textView.TextBuffer.Delete(new Span(_textView.Caret.Position.BufferPosition.Position - indentSize, indentSize)); + return VSConstants.S_OK; + } + } + } + break; + case VSConstants.VSStd2KCmdID.SHOWMEMBERLIST: + case VSConstants.VSStd2KCmdID.COMPLETEWORD: + var controller = _textView.Properties.GetProperty(typeof(IntellisenseController)); + if (controller != null) { + controller.TriggerCompletionSession((VSConstants.VSStd2KCmdID)nCmdID == VSConstants.VSStd2KCmdID.COMPLETEWORD); + return VSConstants.S_OK; + } + break; + case VSConstants.VSStd2KCmdID.OUTLN_STOP_HIDING_ALL: + tagger = _textView.TextBuffer.GetOutliningTagger(); + if (tagger != null) { + tagger.Disable(); + } + // let VS get the event as well + break; + case VSConstants.VSStd2KCmdID.OUTLN_START_AUTOHIDING: + tagger = _textView.TextBuffer.GetOutliningTagger(); + if (tagger != null) { + tagger.Enable(); + } + // let VS get the event as well + break; + case VSConstants.VSStd2KCmdID.COMMENT_BLOCK: + case VSConstants.VSStd2KCmdID.COMMENTBLOCK: + _textView.CommentBlock(); + break; + case VSConstants.VSStd2KCmdID.UNCOMMENT_BLOCK: + case VSConstants.VSStd2KCmdID.UNCOMMENTBLOCK: + _textView.UncommentBlock(); + break; + } + } + + return _next.Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); + } + + /// + /// Called from VS to see what commands we support. + /// + public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { + if (pguidCmdGroup == VSConstants.GUID_VSStandardCommandSet97) { + for (int i = 0; i < cCmds; i++) { + switch ((VSConstants.VSStd97CmdID)prgCmds[i].cmdID) { + case VSConstants.VSStd97CmdID.GotoDefn: + prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); + return VSConstants.S_OK; + case VSConstants.VSStd97CmdID.FindReferences: + prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); + return VSConstants.S_OK; + } + } + } else if (pguidCmdGroup == GuidList.guidIronPythonToolsCmdSet) { + for (int i = 0; i < cCmds; i++) { + foreach (var command in CommandTable.Commands) { + if (command.CommandId == prgCmds[i].cmdID) { + int? res = command.EditFilterQueryStatus(ref prgCmds[i], pCmdText); + if (res != null) { + return res.Value; + } + } + } + } + } else if (pguidCmdGroup == CommonConstants.Std2KCmdGroupGuid) { + OutliningTaggerProvider.OutliningTagger tagger; + for (int i = 0; i < cCmds; i++) { + switch ((VSConstants.VSStd2KCmdID)prgCmds[i].cmdID) { + case VSConstants.VSStd2KCmdID.SHOWMEMBERLIST: + case VSConstants.VSStd2KCmdID.COMPLETEWORD: + prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); + return VSConstants.S_OK; + case VSConstants.VSStd2KCmdID.OUTLN_STOP_HIDING_ALL: + tagger = _textView.TextBuffer.GetOutliningTagger(); + if (tagger != null && tagger.Enabled) { + prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); + } + return VSConstants.S_OK; + case VSConstants.VSStd2KCmdID.OUTLN_START_AUTOHIDING: + tagger = _textView.TextBuffer.GetOutliningTagger(); + if (tagger != null && !tagger.Enabled) { + prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); + } + return VSConstants.S_OK; + case VSConstants.VSStd2KCmdID.COMMENT_BLOCK: + case VSConstants.VSStd2KCmdID.COMMENTBLOCK: + case VSConstants.VSStd2KCmdID.UNCOMMENT_BLOCK: + case VSConstants.VSStd2KCmdID.UNCOMMENTBLOCK: + prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); + return VSConstants.S_OK; + } + } + } + return _next.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/PythonLanguageInfo.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/PythonLanguageInfo.cs new file mode 100644 index 0000000000..2754ae2c3a --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/PythonLanguageInfo.cs @@ -0,0 +1,81 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.TextManager.Interop; + +namespace Microsoft.IronPythonTools.Navigation { + /// + /// Minimal language service. Implemented directly rather than using the Managed Package + /// Framework because we don't want to provide colorization services. Instead we use the + /// new Visual Studio 2010 APIs to provide these services. But we still need this to + /// provide a code window manager so that we can have a navigation bar (actually we don't, this + /// should be switched over to using our TextViewCreationListener instead). + /// + internal sealed class PythonLanguageInfo : IVsLanguageInfo { + private readonly IServiceProvider _serviceProvider; + private readonly IComponentModel _componentModel; + + public PythonLanguageInfo(IServiceProvider serviceProvider) { + _serviceProvider = serviceProvider; + _componentModel = serviceProvider.GetService(typeof(SComponentModel)) as IComponentModel; + } + + public int GetCodeWindowManager(IVsCodeWindow pCodeWin, out IVsCodeWindowManager ppCodeWinMgr) { + var model = _serviceProvider.GetService(typeof(SComponentModel)) as IComponentModel; + var service = model.GetService(); + + IVsTextView textView; + if (ErrorHandler.Succeeded(pCodeWin.GetPrimaryView(out textView))) { + ppCodeWinMgr = new CodeWindowManager(pCodeWin, service.GetWpfTextView(textView), _componentModel); + + return VSConstants.S_OK; + } + + ppCodeWinMgr = null; + return VSConstants.E_FAIL; + } + + public int GetFileExtensions(out string pbstrExtensions) { + // This is the same extension the language service was + // registered as supporting. + pbstrExtensions = ".py"; + return VSConstants.S_OK; + } + + + public int GetLanguageName(out string bstrName) { + // This is the same name the language service was registered with. + bstrName = PythonConstants.LanguageName; + return VSConstants.S_OK; + } + + /// + /// GetColorizer is not implemented because we implement colorization using the new managed APIs. + /// + public int GetColorizer(IVsTextLines pBuffer, out IVsColorizer ppColorizer) { + ppColorizer = null; + return VSConstants.E_FAIL; + } + + public IServiceProvider ServiceProvider { + get { + return _serviceProvider; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/PythonLibraryManager.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/PythonLibraryManager.cs new file mode 100644 index 0000000000..c790ef948a --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/PythonLibraryManager.cs @@ -0,0 +1,68 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ +using System; +using System.Runtime.InteropServices; +using Microsoft.IronStudio.Navigation; +using Microsoft.PyAnalysis; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.IronPythonTools.Navigation { + + /// + /// This interface defines the service that finds IronPython files inside a hierarchy + /// and builds the informations to expose to the class view or object browser. + /// + [Guid(PythonConstants.LibraryManagerServiceGuid)] + public interface IPythonLibraryManager : ILibraryManager { + } + + /// + /// Implementation of the service that builds the information to expose to the symbols + /// navigation tools (class view or object browser) from the Python files inside a + /// hierarchy. + /// + [Guid(PythonConstants.LibraryManagerGuid)] + internal class PythonLibraryManager : LibraryManager, IPythonLibraryManager { + private readonly IronPythonToolsPackage/*!*/ _package; + + public PythonLibraryManager(IronPythonToolsPackage/*!*/ package) + : base(package) { + _package = package; + } + + protected override LibraryNode CreateLibraryNode(IScopeNode subItem, string namePrefix, IVsHierarchy hierarchy, uint itemid) { + return new PythonLibraryNode(subItem, namePrefix, hierarchy, itemid); + } + + protected override void OnNewFile(LibraryTask task) { + IProjectEntry item; + if (task.TextBuffer != null) { + item = task.TextBuffer.GetAnalysis(); + } else { + item = IronPythonToolsPackage.Instance.Analyzer.AnalyzeFile(task.FileName); + } + + IPythonProjectEntry pyCode; + if (item != null && (pyCode = item as IPythonProjectEntry) != null) { + // We subscribe to OnNewAnalysis here instead of OnNewParseTree so that + // in the future we can use the analysis to include type information in the + // object browser (for example we could include base type information with + // links elsewhere in the object browser). + pyCode.OnNewAnalysis += (sender, args) => { + FileParsed(task, new AstScopeNode(pyCode.Tree, pyCode)); + }; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/PythonLibraryNode.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/PythonLibraryNode.cs new file mode 100644 index 0000000000..f6512e7339 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Navigation/PythonLibraryNode.cs @@ -0,0 +1,110 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Text; +using IronPython.Compiler.Ast; +using IronPython.Runtime; +using IronPython.Runtime.Operations; +using Microsoft.IronStudio.Navigation; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.IronPythonTools.Navigation { + public class PythonLibraryNode : CommonLibraryNode { + public PythonLibraryNode(IScopeNode scope, string namePrefix, IVsHierarchy hierarchy, uint itemId) + : base(scope, namePrefix, hierarchy, itemId) { } + + protected PythonLibraryNode(PythonLibraryNode node) : base(node) { } + + protected override LibraryNode Clone() { + return new PythonLibraryNode(this); + } + + public override StandardGlyphGroup GlyphType { + get { + if (ScopeNode is FunctionScopeNode) { + return StandardGlyphGroup.GlyphGroupMethod; + } + + return StandardGlyphGroup.GlyphGroupClass; + } + } + + public override string GetTextRepresentation(VSTREETEXTOPTIONS options) { + FunctionScopeNode funcScope = ScopeNode as FunctionScopeNode; + if (funcScope != null) { + StringBuilder sb = new StringBuilder(); + GetFunctionDescription(funcScope.Definition, (text, kind, arg) => { + sb.Append(text); + }); + return sb.ToString(); + } + + return Name; + } + + public override void FillDescription(_VSOBJDESCOPTIONS flags, IVsObjectBrowserDescription3 description) { + description.ClearDescriptionText(); + FunctionScopeNode funcScope = ScopeNode as FunctionScopeNode; + if (funcScope != null) { + description.AddDescriptionText3("def ", VSOBDESCRIPTIONSECTION.OBDS_MISC, null); + var def = funcScope.Definition; + GetFunctionDescription(def, (text, kind, arg) => { + description.AddDescriptionText3(text, kind, arg); + }); + description.AddDescriptionText3(null, VSOBDESCRIPTIONSECTION.OBDS_ENDDECL, null); + if (def.Body.Documentation != null) { + description.AddDescriptionText3(" " + def.Body.Documentation, VSOBDESCRIPTIONSECTION.OBDS_MISC, null); + } + } else { + description.AddDescriptionText3("class ", VSOBDESCRIPTIONSECTION.OBDS_MISC, null); + description.AddDescriptionText3(ScopeNode.Name, VSOBDESCRIPTIONSECTION.OBDS_NAME, null); + } + } + + private void GetFunctionDescription(FunctionDefinition def, Action addDescription) { + addDescription(ScopeNode.Name, VSOBDESCRIPTIONSECTION.OBDS_NAME, null); + addDescription("(", VSOBDESCRIPTIONSECTION.OBDS_MISC, null); + + for (int i = 0; i < def.Parameters.Count; i++) { + if (i != 0) { + addDescription(", ", VSOBDESCRIPTIONSECTION.OBDS_MISC, null); + } + + var curParam = def.Parameters[i]; + + string name = curParam.Name; + if (curParam.IsDictionary) { + name = "**" + name; + } else if (curParam.IsList) { + name = "*" + curParam.Name; + } + + if (curParam.DefaultValue != null) { + // TODO: Support all possible expressions for default values, we should + // probably have a PythonAst walker for expressions or we should add ToCodeString() + // onto Python ASTs so they can round trip + ConstantExpression defaultValue = curParam.DefaultValue as ConstantExpression; + if (defaultValue != null) { + name = name + " = " + PythonOps.Repr(DefaultContext.Default, defaultValue.Value); + } + } + + addDescription(name, VSOBDESCRIPTIONSECTION.OBDS_PARAM, null); + } + addDescription(")\n", VSOBDESCRIPTIONSECTION.OBDS_MISC, null); + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonDialogPage.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonDialogPage.cs new file mode 100644 index 0000000000..49b7199169 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonDialogPage.cs @@ -0,0 +1,98 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.IronPythonTools.Options { + /// + /// Base class used for saving/loading of settings. The settings are stored in VSRegistryRoot\IronPython\Options\Category\SettingName + /// where Category is provided in the constructor and SettingName is provided to each call of the Save*/Load* APIs. + /// + /// The primary purpose of this class is so that we can be in control of providing reasonable default values. + /// + class PythonDialogPage : DialogPage { + private readonly string _category; + private const string _optionsKey = "Options"; + + public PythonDialogPage(string category) { + _category = category; + } + + public void SaveBool(string name, bool value) { + SaveString(name, value.ToString()); + } + + public void SaveInt(string name, int value) { + SaveString(name, value.ToString()); + } + + public void SaveString(string name, string value) { + using (var ironPython = VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_UserSettings, true).CreateSubKey("IronPython")) { + using (var optionsKey = ironPython.CreateSubKey(_optionsKey)) { + using (var categoryKey = optionsKey.CreateSubKey(_category)) { + categoryKey.SetValue(name, value, Win32.RegistryValueKind.String); + } + } + } + } + + public void SaveEnum(string name, T value) where T : struct { + SaveString(name, value.ToString()); + } + + public int? LoadInt(string name) { + string res = LoadString(name); + if (res == null) { + return null; + } + return Convert.ToInt32(res); + } + + public bool? LoadBool(string name) { + string res = LoadString(name); + if (res == null) { + return null; + } + return Convert.ToBoolean(res); + } + + public string LoadString(string name) { + using (var ironPython = VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_UserSettings, true).CreateSubKey("IronPython")) { + using (var optionsKey = ironPython.CreateSubKey(_optionsKey)) { + using (var categoryKey = optionsKey.CreateSubKey(_category)) { + return categoryKey.GetValue(name) as string; + } + } + } + } + + public T? LoadEnum(string name) where T : struct { + string res = LoadString(name); + if (res == null) { + return null; + } + + T enumRes; + if (Enum.TryParse(res, out enumRes)) { + return enumRes; + } + return null; + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonIntellisenseOptionsControl.Designer.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonIntellisenseOptionsControl.Designer.cs new file mode 100644 index 0000000000..5f82299cad --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonIntellisenseOptionsControl.Designer.cs @@ -0,0 +1,136 @@ +namespace Microsoft.IronPythonTools.Options { + partial class PythonIntellisenseOptionsControl { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this._completionCommitedBy = new System.Windows.Forms.TextBox(); + this._completionCommitedByLabel = new System.Windows.Forms.Label(); + this._enterCommits = new System.Windows.Forms.CheckBox(); + this._intersectMembers = new System.Windows.Forms.CheckBox(); + this._selectionInCompletionGroupBox = new System.Windows.Forms.GroupBox(); + this._newLineAfterCompleteCompletion = new System.Windows.Forms.CheckBox(); + this._completionResultsGroupBox = new System.Windows.Forms.GroupBox(); + this._selectionInCompletionGroupBox.SuspendLayout(); + this._completionResultsGroupBox.SuspendLayout(); + this.SuspendLayout(); + // + // _completionCommitedBy + // + this._completionCommitedBy.Location = new System.Drawing.Point(12, 32); + this._completionCommitedBy.Name = "_completionCommitedBy"; + this._completionCommitedBy.Size = new System.Drawing.Size(371, 20); + this._completionCommitedBy.TabIndex = 1; + this._completionCommitedBy.TextChanged += new System.EventHandler(this._completionCommitedBy_TextChanged); + // + // _completionCommitedByLabel + // + this._completionCommitedByLabel.AutoSize = true; + this._completionCommitedByLabel.Location = new System.Drawing.Point(12, 16); + this._completionCommitedByLabel.Name = "_completionCommitedByLabel"; + this._completionCommitedByLabel.Size = new System.Drawing.Size(219, 13); + this._completionCommitedByLabel.TabIndex = 0; + this._completionCommitedByLabel.Text = "Committed by &typing the following characters:"; + // + // _enterCommits + // + this._enterCommits.AutoSize = true; + this._enterCommits.Location = new System.Drawing.Point(12, 58); + this._enterCommits.Name = "_enterCommits"; + this._enterCommits.Size = new System.Drawing.Size(182, 17); + this._enterCommits.TabIndex = 2; + this._enterCommits.Text = "&Enter commits current completion"; + this._enterCommits.UseVisualStyleBackColor = true; + this._enterCommits.CheckedChanged += new System.EventHandler(this._enterCommits_CheckedChanged); + // + // _intersectMembers + // + this._intersectMembers.AutoSize = true; + this._intersectMembers.Location = new System.Drawing.Point(12, 19); + this._intersectMembers.Name = "_intersectMembers"; + this._intersectMembers.Size = new System.Drawing.Size(272, 17); + this._intersectMembers.TabIndex = 0; + this._intersectMembers.Text = "Member completion displays &intersection of members"; + this._intersectMembers.UseVisualStyleBackColor = true; + this._intersectMembers.CheckedChanged += new System.EventHandler(this._intersectMembers_CheckedChanged); + // + // _selectionInCompletionGroupBox + // + this._selectionInCompletionGroupBox.Controls.Add(this._completionCommitedByLabel); + this._selectionInCompletionGroupBox.Controls.Add(this._completionCommitedBy); + this._selectionInCompletionGroupBox.Controls.Add(this._enterCommits); + this._selectionInCompletionGroupBox.Controls.Add(this._newLineAfterCompleteCompletion); + this._selectionInCompletionGroupBox.Location = new System.Drawing.Point(3, 55); + this._selectionInCompletionGroupBox.Name = "_selectionInCompletionGroupBox"; + this._selectionInCompletionGroupBox.Size = new System.Drawing.Size(389, 110); + this._selectionInCompletionGroupBox.TabIndex = 1; + this._selectionInCompletionGroupBox.TabStop = false; + this._selectionInCompletionGroupBox.Text = "Selection in Completion List"; + // + // _newLineAfterCompleteCompletion + // + this._newLineAfterCompleteCompletion.AutoSize = true; + this._newLineAfterCompleteCompletion.Location = new System.Drawing.Point(12, 82); + this._newLineAfterCompleteCompletion.Name = "_newLineAfterCompleteCompletion"; + this._newLineAfterCompleteCompletion.Size = new System.Drawing.Size(250, 17); + this._newLineAfterCompleteCompletion.TabIndex = 3; + this._newLineAfterCompleteCompletion.Text = "&Add new line on enter at end of fully typed word"; + this._newLineAfterCompleteCompletion.UseVisualStyleBackColor = true; + this._newLineAfterCompleteCompletion.CheckedChanged += new System.EventHandler(this._newLineAfterCompleteCompletion_CheckedChanged); + // + // _completionResultsGroupBox + // + this._completionResultsGroupBox.Controls.Add(this._intersectMembers); + this._completionResultsGroupBox.Location = new System.Drawing.Point(3, 3); + this._completionResultsGroupBox.Name = "_completionResultsGroupBox"; + this._completionResultsGroupBox.Size = new System.Drawing.Size(389, 46); + this._completionResultsGroupBox.TabIndex = 0; + this._completionResultsGroupBox.TabStop = false; + this._completionResultsGroupBox.Text = "Completion Results"; + // + // PythonIntellisenseOptionsControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this._completionResultsGroupBox); + this.Controls.Add(this._selectionInCompletionGroupBox); + this.Name = "PythonIntellisenseOptionsControl"; + this.Size = new System.Drawing.Size(395, 317); + this._selectionInCompletionGroupBox.ResumeLayout(false); + this._selectionInCompletionGroupBox.PerformLayout(); + this._completionResultsGroupBox.ResumeLayout(false); + this._completionResultsGroupBox.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.CheckBox _enterCommits; + private System.Windows.Forms.CheckBox _intersectMembers; + private System.Windows.Forms.TextBox _completionCommitedBy; + private System.Windows.Forms.Label _completionCommitedByLabel; + private System.Windows.Forms.GroupBox _selectionInCompletionGroupBox; + private System.Windows.Forms.GroupBox _completionResultsGroupBox; + private System.Windows.Forms.CheckBox _newLineAfterCompleteCompletion; + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonIntellisenseOptionsControl.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonIntellisenseOptionsControl.cs new file mode 100644 index 0000000000..9ffc6267cc --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonIntellisenseOptionsControl.cs @@ -0,0 +1,45 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Windows.Forms; +using Microsoft.IronStudio.Core.Repl; + +namespace Microsoft.IronPythonTools.Options { + public partial class PythonIntellisenseOptionsControl : UserControl { + public PythonIntellisenseOptionsControl() { + InitializeComponent(); + _enterCommits.Checked = IronPythonToolsPackage.Instance.IntellisenseOptionsPage.EnterCommitsIntellisense; + _intersectMembers.Checked = IronPythonToolsPackage.Instance.IntellisenseOptionsPage.IntersectMembers; + _completionCommitedBy.Text = IronPythonToolsPackage.Instance.IntellisenseOptionsPage.CompletionCommittedBy; + _newLineAfterCompleteCompletion.Checked = IronPythonToolsPackage.Instance.IntellisenseOptionsPage.AddNewLineAtEndOfFullyTypedWord; + } + + private void _enterCommits_CheckedChanged(object sender, EventArgs e) { + IronPythonToolsPackage.Instance.IntellisenseOptionsPage.EnterCommitsIntellisense = _enterCommits.Checked; + } + + private void _intersectMembers_CheckedChanged(object sender, EventArgs e) { + IronPythonToolsPackage.Instance.IntellisenseOptionsPage.IntersectMembers = _intersectMembers.Checked; + } + + private void _completionCommitedBy_TextChanged(object sender, EventArgs e) { + IronPythonToolsPackage.Instance.IntellisenseOptionsPage.CompletionCommittedBy = _completionCommitedBy.Text; + } + + private void _newLineAfterCompleteCompletion_CheckedChanged(object sender, EventArgs e) { + IronPythonToolsPackage.Instance.IntellisenseOptionsPage.AddNewLineAtEndOfFullyTypedWord = _newLineAfterCompleteCompletion.Checked; + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonIntellisenseOptionsControl.resx b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonIntellisenseOptionsControl.resx new file mode 100644 index 0000000000..29dcb1b3a3 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonIntellisenseOptionsControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonIntellisenseOptionsPage.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonIntellisenseOptionsPage.cs new file mode 100644 index 0000000000..895779e654 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonIntellisenseOptionsPage.cs @@ -0,0 +1,95 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel; +using Microsoft.VisualStudio.Shell; +using Microsoft.IronPythonTools.Commands; +using Microsoft.IronStudio.Core.Repl; + +namespace Microsoft.IronPythonTools.Options { + class PythonIntellisenseOptionsPage : PythonDialogPage { + private bool _enterCommitsIntellisense, _intersectMembers, _addNewLineAtEndOfFullyTypedWord; + private PythonIntellisenseOptionsControl _window; + private string _completionCommittedBy; + private const string _defaultCompletionChars = "{}[]().,:;+-*/%&|^~=<>#'\"\\"; + + public PythonIntellisenseOptionsPage() + : base("Intellisense") { + } + + // replace the default UI of the dialog page w/ our own UI. + protected override System.Windows.Forms.IWin32Window Window { + get { + if (_window == null) { + _window = new PythonIntellisenseOptionsControl(); + } + return _window; + } + } + + #region Intellisense Options + + public bool EnterCommitsIntellisense { + get { return _enterCommitsIntellisense; } + set { _enterCommitsIntellisense = value; } + } + + public bool IntersectMembers { + get { return _intersectMembers; } + set { + _intersectMembers = value; + + IronPythonToolsPackage.Instance.RuntimeHost.IntersectMembers = IntersectMembers; + } + } + + public bool AddNewLineAtEndOfFullyTypedWord { + get { return _addNewLineAtEndOfFullyTypedWord; } + set { _addNewLineAtEndOfFullyTypedWord = value; } + } + + public string CompletionCommittedBy { + get { return _completionCommittedBy; } + set { _completionCommittedBy = value; } + } + + #endregion + + public override void ResetSettings() { + _enterCommitsIntellisense = true; + _intersectMembers = true; + _addNewLineAtEndOfFullyTypedWord = false; + _completionCommittedBy = _defaultCompletionChars; + } + + private const string EnterCommitsSetting = "EnterCommits"; + private const string IntersectMembersSetting = "IntersectMembers"; + private const string NewLineAtEndOfWordSetting = "NewLineAtEndOfWord"; + private const string CompletionCommittedBySetting = "CompletionCommittedBy"; + + public override void LoadSettingsFromStorage() { + _enterCommitsIntellisense = LoadBool(EnterCommitsSetting) ?? true; + _intersectMembers = LoadBool(IntersectMembersSetting) ?? true; + _addNewLineAtEndOfFullyTypedWord = LoadBool(NewLineAtEndOfWordSetting) ?? false; + _completionCommittedBy = LoadString("CompletionCommittedBy") ?? _defaultCompletionChars; + } + + public override void SaveSettingsToStorage() { + SaveBool(EnterCommitsSetting, _enterCommitsIntellisense); + SaveBool(IntersectMembersSetting, _intersectMembers); + SaveBool(NewLineAtEndOfWordSetting, _addNewLineAtEndOfFullyTypedWord); + SaveString(CompletionCommittedBySetting, _defaultCompletionChars); + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonOptionsControl.Designer.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonOptionsControl.Designer.cs new file mode 100644 index 0000000000..f69d0d619d --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonOptionsControl.Designer.cs @@ -0,0 +1,208 @@ +namespace Microsoft.IronPythonTools.Options { + partial class PythonOptionsControl { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.components = new System.ComponentModel.Container(); + this._editorGroup = new System.Windows.Forms.GroupBox(); + this._outliningOnOpen = new System.Windows.Forms.CheckBox(); + this._fillParagraphText = new System.Windows.Forms.TextBox(); + this._fillParaColumnLabel = new System.Windows.Forms.Label(); + this._interactiveGroup = new System.Windows.Forms.GroupBox(); + this._smartReplHistory = new System.Windows.Forms.CheckBox(); + this._completionModeGroup = new System.Windows.Forms.GroupBox(); + this._evalAlways = new System.Windows.Forms.RadioButton(); + this._evalNoCalls = new System.Windows.Forms.RadioButton(); + this._evalNever = new System.Windows.Forms.RadioButton(); + this._interactiveOptions = new System.Windows.Forms.Label(); + this._interactiveOptionsValue = new System.Windows.Forms.TextBox(); + this._toolTips = new System.Windows.Forms.ToolTip(this.components); + this._editorGroup.SuspendLayout(); + this._interactiveGroup.SuspendLayout(); + this._completionModeGroup.SuspendLayout(); + this.SuspendLayout(); + // + // _editorGroup + // + this._editorGroup.Controls.Add(this._outliningOnOpen); + this._editorGroup.Controls.Add(this._fillParagraphText); + this._editorGroup.Controls.Add(this._fillParaColumnLabel); + this._editorGroup.Location = new System.Drawing.Point(3, 3); + this._editorGroup.Name = "_editorGroup"; + this._editorGroup.Size = new System.Drawing.Size(389, 70); + this._editorGroup.TabIndex = 0; + this._editorGroup.TabStop = false; + this._editorGroup.Text = "Editor"; + // + // _outliningOnOpen + // + this._outliningOnOpen.AutoSize = true; + this._outliningOnOpen.Location = new System.Drawing.Point(12, 20); + this._outliningOnOpen.Name = "_outliningOnOpen"; + this._outliningOnOpen.Size = new System.Drawing.Size(199, 17); + this._outliningOnOpen.TabIndex = 0; + this._outliningOnOpen.Text = "Enter &outlining mode when files open"; + this._outliningOnOpen.UseVisualStyleBackColor = true; + this._outliningOnOpen.CheckedChanged += new System.EventHandler(this._outliningOnOpen_CheckedChanged); + // + // _fillParagraphText + // + this._fillParagraphText.Location = new System.Drawing.Point(133, 41); + this._fillParagraphText.Name = "_fillParagraphText"; + this._fillParagraphText.Size = new System.Drawing.Size(58, 20); + this._fillParagraphText.TabIndex = 2; + this._fillParagraphText.TextChanged += new System.EventHandler(this._fillParagraphText_TextChanged); + // + // _fillParaColumnLabel + // + this._fillParaColumnLabel.AutoSize = true; + this._fillParaColumnLabel.Location = new System.Drawing.Point(12, 44); + this._fillParaColumnLabel.Name = "_fillParaColumnLabel"; + this._fillParaColumnLabel.Size = new System.Drawing.Size(117, 13); + this._fillParaColumnLabel.TabIndex = 1; + this._fillParaColumnLabel.Text = "Fill Paragraph Columns:"; + // + // _interactiveGroup + // + this._interactiveGroup.Controls.Add(this._smartReplHistory); + this._interactiveGroup.Controls.Add(this._interactiveOptionsValue); + this._interactiveGroup.Controls.Add(this._interactiveOptions); + this._interactiveGroup.Controls.Add(this._completionModeGroup); + this._interactiveGroup.Location = new System.Drawing.Point(3, 79); + this._interactiveGroup.Name = "_interactiveGroup"; + this._interactiveGroup.Size = new System.Drawing.Size(389, 160); + this._interactiveGroup.TabIndex = 1; + this._interactiveGroup.TabStop = false; + this._interactiveGroup.Text = "Interactive Window"; + // + // _smartReplHistory + // + this._smartReplHistory.AutoSize = true; + this._smartReplHistory.Location = new System.Drawing.Point(12, 20); + this._smartReplHistory.Name = "_smartReplHistory"; + this._smartReplHistory.Size = new System.Drawing.Size(210, 17); + this._smartReplHistory.TabIndex = 0; + this._smartReplHistory.Text = "Up/Down Arrow Keys use smart &history"; + this._smartReplHistory.UseVisualStyleBackColor = true; + this._smartReplHistory.CheckedChanged += new System.EventHandler(this._smartReplHistory_CheckedChanged); + // + // _interactiveOptions + // + this._interactiveOptions.AutoSize = true; + this._interactiveOptions.Location = new System.Drawing.Point(12, 40); + this._interactiveOptions.Name = "_interactiveOptions"; + this._interactiveOptions.Size = new System.Drawing.Size(46, 13); + this._interactiveOptions.TabIndex = 1; + this._interactiveOptions.Text = "Options:"; + // + // _interactiveOptionsValue + // + this._interactiveOptionsValue.Location = new System.Drawing.Point(69, 40); + this._interactiveOptionsValue.Name = "_interactiveOptionsValue"; + this._interactiveOptionsValue.Size = new System.Drawing.Size(314, 20); + this._interactiveOptionsValue.TabIndex = 2; + this._interactiveOptionsValue.TextChanged += new System.EventHandler(this._interactiveOptionsValue_TextChanged); + // + // _completionModeGroup + // + this._completionModeGroup.Controls.Add(this._evalAlways); + this._completionModeGroup.Controls.Add(this._evalNoCalls); + this._completionModeGroup.Controls.Add(this._evalNever); + this._completionModeGroup.Location = new System.Drawing.Point(19, 64); + this._completionModeGroup.Name = "_completionModeGroup"; + this._completionModeGroup.Size = new System.Drawing.Size(364, 86); + this._completionModeGroup.TabIndex = 3; + this._completionModeGroup.TabStop = false; + this._completionModeGroup.Text = "Completion Mode"; + // + // _evalAlways + // + this._evalAlways.AutoSize = true; + this._evalAlways.Location = new System.Drawing.Point(12, 58); + this._evalAlways.Name = "_evalAlways"; + this._evalAlways.Size = new System.Drawing.Size(160, 17); + this._evalAlways.TabIndex = 20; + this._evalAlways.TabStop = true; + this._evalAlways.Text = "&Always evaluate expressions"; + this._evalAlways.UseVisualStyleBackColor = true; + this._evalAlways.CheckedChanged += new System.EventHandler(this._evalAlways_CheckedChanged); + // + // _evalNoCalls + // + this._evalNoCalls.AutoSize = true; + this._evalNoCalls.Location = new System.Drawing.Point(12, 39); + this._evalNoCalls.Name = "_evalNoCalls"; + this._evalNoCalls.Size = new System.Drawing.Size(232, 17); + this._evalNoCalls.TabIndex = 10; + this._evalNoCalls.TabStop = true; + this._evalNoCalls.Text = "Never evaluate expressions containing &calls"; + this._evalNoCalls.UseVisualStyleBackColor = true; + this._evalNoCalls.CheckedChanged += new System.EventHandler(this._evalNoCalls_CheckedChanged); + // + // _evalNever + // + this._evalNever.AutoSize = true; + this._evalNever.Location = new System.Drawing.Point(12, 20); + this._evalNever.Name = "_evalNever"; + this._evalNever.Size = new System.Drawing.Size(156, 17); + this._evalNever.TabIndex = 0; + this._evalNever.TabStop = true; + this._evalNever.Text = "&Never evaluate expressions"; + this._evalNever.UseVisualStyleBackColor = true; + this._evalNever.CheckedChanged += new System.EventHandler(this._evalNever_CheckedChanged); + // + // PythonOptionsControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this._editorGroup); + this.Controls.Add(this._interactiveGroup); + this.Name = "PythonOptionsControl"; + this.Size = new System.Drawing.Size(395, 317); + this._editorGroup.ResumeLayout(false); + this._editorGroup.PerformLayout(); + this._interactiveGroup.ResumeLayout(false); + this._interactiveGroup.PerformLayout(); + this._completionModeGroup.ResumeLayout(false); + this._completionModeGroup.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.GroupBox _editorGroup; + private System.Windows.Forms.GroupBox _interactiveGroup; + private System.Windows.Forms.CheckBox _outliningOnOpen; + private System.Windows.Forms.CheckBox _smartReplHistory; + private System.Windows.Forms.GroupBox _completionModeGroup; + private System.Windows.Forms.RadioButton _evalAlways; + private System.Windows.Forms.RadioButton _evalNoCalls; + private System.Windows.Forms.RadioButton _evalNever; + private System.Windows.Forms.Label _fillParaColumnLabel; + private System.Windows.Forms.TextBox _fillParagraphText; + private System.Windows.Forms.Label _interactiveOptions; + private System.Windows.Forms.TextBox _interactiveOptionsValue; + private System.Windows.Forms.ToolTip _toolTips; + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonOptionsControl.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonOptionsControl.cs new file mode 100644 index 0000000000..58305a3912 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonOptionsControl.cs @@ -0,0 +1,76 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Windows.Forms; +using Microsoft.IronStudio.Core.Repl; + +namespace Microsoft.IronPythonTools.Options { + public partial class PythonOptionsControl : UserControl { + + public PythonOptionsControl() { + InitializeComponent(); + _outliningOnOpen.Checked = IronPythonToolsPackage.Instance.OptionsPage.EnterOutliningModeOnOpen; + _smartReplHistory.Checked = IronPythonToolsPackage.Instance.OptionsPage.ReplSmartHistory; + _fillParagraphText.Text = IronPythonToolsPackage.Instance.OptionsPage.FillParagraphColumns.ToString(); + _interactiveOptionsValue.Text = IronPythonToolsPackage.Instance.OptionsPage.InteractiveOptions; + + switch (IronPythonToolsPackage.Instance.OptionsPage.ReplIntellisenseMode) { + case ReplIntellisenseMode.AlwaysEvaluate: _evalAlways.Checked = true; break; + case ReplIntellisenseMode.DontEvaluateCalls: _evalNoCalls.Checked = true; break; + case ReplIntellisenseMode.NeverEvaluate: _evalNever.Checked = true; break; + } + + const string optionsToolTip = "Options for the interactive window process. For example: Frames=True;RecursionLimit=1001\r\n\r\nChanges take effect after interactive window reset."; + _toolTips.SetToolTip(_interactiveOptionsValue, optionsToolTip); + _toolTips.SetToolTip(_interactiveOptions, optionsToolTip); + } + + private void _outliningOnOpen_CheckedChanged(object sender, EventArgs e) { + IronPythonToolsPackage.Instance.OptionsPage.EnterOutliningModeOnOpen = _outliningOnOpen.Checked; + } + + private void _smartReplHistory_CheckedChanged(object sender, EventArgs e) { + IronPythonToolsPackage.Instance.OptionsPage.ReplSmartHistory = _smartReplHistory.Checked; + } + + private void _evalNever_CheckedChanged(object sender, EventArgs e) { + IronPythonToolsPackage.Instance.OptionsPage.ReplIntellisenseMode = ReplIntellisenseMode.NeverEvaluate; + } + + private void _evalNoCalls_CheckedChanged(object sender, EventArgs e) { + IronPythonToolsPackage.Instance.OptionsPage.ReplIntellisenseMode = ReplIntellisenseMode.DontEvaluateCalls; + } + + private void _evalAlways_CheckedChanged(object sender, EventArgs e) { + IronPythonToolsPackage.Instance.OptionsPage.ReplIntellisenseMode = ReplIntellisenseMode.AlwaysEvaluate; + } + + private void _interactiveOptionsValue_TextChanged(object sender, EventArgs e) { + IronPythonToolsPackage.Instance.OptionsPage.InteractiveOptions = _interactiveOptionsValue.Text; + } + + private void _fillParagraphText_TextChanged(object sender, EventArgs e) { + for (int i = 0; i < _fillParagraphText.Text.Length; i++) { + if (!Char.IsDigit(_fillParagraphText.Text[i])) { + _fillParagraphText.Text = IronPythonToolsPackage.Instance.OptionsPage.FillParagraphColumns.ToString(); + return; + } + } + if (_fillParagraphText.Text.Length != 0) { + IronPythonToolsPackage.Instance.OptionsPage.FillParagraphColumns = Convert.ToInt32(_fillParagraphText.Text); + } + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonOptionsControl.resx b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonOptionsControl.resx new file mode 100644 index 0000000000..c2446bc58b --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonOptionsControl.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonOptionsPage.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonOptionsPage.cs new file mode 100644 index 0000000000..5f3fec7c43 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Options/PythonOptionsPage.cs @@ -0,0 +1,114 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.IronPythonTools.Commands; +using Microsoft.IronStudio.Core.Repl; + +namespace Microsoft.IronPythonTools.Options { + class PythonOptionsPage : PythonDialogPage { + private bool _enterOutliningMode, _smartHistory; + private ReplIntellisenseMode _replIntellisenseMode; + private PythonOptionsControl _window; + private int _fillParagraphColumns; + private string _interactiveOptions; + + public PythonOptionsPage() + : base("Advanced") { + } + + // replace the default UI of the dialog page w/ our own UI. + protected override System.Windows.Forms.IWin32Window Window { + get { + if (_window == null) { + _window = new PythonOptionsControl(); + } + return _window; + } + } + + #region Editor Options + + public bool EnterOutliningModeOnOpen { + get { return _enterOutliningMode; } + set { + _enterOutliningMode = value; + + IronPythonToolsPackage.Instance.RuntimeHost.EnterOutliningModeOnOpen = EnterOutliningModeOnOpen; + } + } + + #endregion + + #region Repl Options + + public bool ReplSmartHistory { + get { return _smartHistory; } + set { + _smartHistory = value; + + // propagate changes + var repl = ExecuteInReplCommand.TryGetReplWindow(); + if (repl != null) { + repl.UseSmartUpDown = ReplSmartHistory; + } + } + } + + public ReplIntellisenseMode ReplIntellisenseMode { + get { return _replIntellisenseMode; } + set { _replIntellisenseMode = value; } + } + + public int FillParagraphColumns { + get { return _fillParagraphColumns; } + set { _fillParagraphColumns = value; } + } + + public string InteractiveOptions { + get { return _interactiveOptions; } + set { _interactiveOptions = value; } + } + + #endregion + + public override void ResetSettings() { + _enterOutliningMode = true; + _smartHistory = true; + _replIntellisenseMode = ReplIntellisenseMode.DontEvaluateCalls; + } + + private const string EnterOutlingModeOnOpenSetting = "EnterOutlingModeOnOpen"; + private const string SmartHistorySetting = "InteractiveSmartHistory"; + private const string ReplIntellisenseModeSetting = "InteractiveIntellisenseMode"; + private const string FillParagraphColumnsSetting = "FillParagraphColumns"; + private const string InteractiveOptionsSetting = "InteractiveOptions"; + + public override void LoadSettingsFromStorage() { + _enterOutliningMode = LoadBool(EnterOutlingModeOnOpenSetting) ?? true; + _smartHistory = LoadBool(SmartHistorySetting) ?? true; + _replIntellisenseMode = LoadEnum(ReplIntellisenseModeSetting) ?? ReplIntellisenseMode.DontEvaluateCalls; + _fillParagraphColumns = LoadInt(FillParagraphColumnsSetting) ?? 80; + _interactiveOptions = LoadString(InteractiveOptionsSetting) ?? String.Empty; + } + + public override void SaveSettingsToStorage() { + SaveBool(EnterOutlingModeOnOpenSetting, _enterOutliningMode); + SaveBool(SmartHistorySetting, _smartHistory); + SaveEnum(ReplIntellisenseModeSetting, _replIntellisenseMode); + SaveInt(FillParagraphColumnsSetting, _fillParagraphColumns); + SaveString(InteractiveOptionsSetting, _interactiveOptions); + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/IPythonStarter.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/IPythonStarter.cs new file mode 100644 index 0000000000..66423875a3 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/IPythonStarter.cs @@ -0,0 +1,23 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using Microsoft.IronStudio.Project; + +namespace Microsoft.IronPythonTools.Navigation { + /// + /// This is a marker interface required to query Python starter + /// service proffered by Python project system package. + /// + public interface IPythonStarter : IStarter {} +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonEditorFactory.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonEditorFactory.cs new file mode 100644 index 0000000000..2718ed12e4 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonEditorFactory.cs @@ -0,0 +1,31 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using Microsoft.IronStudio.Project; + + +namespace Microsoft.IronPythonTools.Project { + /// + /// Factory for creating code editor. + /// + /// + /// While currently empty, editor factory has to be unique per language. + /// + [Guid(PythonConstants.EditorFactoryGuid)] + public class PythonEditorFactory : CommonEditorFactory { + public PythonEditorFactory(CommonProjectPackage package) : base(package) { } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonFileNode.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonFileNode.cs new file mode 100644 index 0000000000..347f07e716 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonFileNode.cs @@ -0,0 +1,82 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.IO; +using System.Text; +using Microsoft.IronStudio.Project; +using Microsoft.PyAnalysis; +using Microsoft.VisualStudio.Project; + +namespace Microsoft.IronPythonTools.Project { + + public class PythonFileNode : CommonFileNode { + internal PythonFileNode(CommonProjectNode root, ProjectElement e) + : base(root, e) { } + + public override string Caption { + get { + var res = base.Caption; + if (res == "__init__.py" && Parent != null) { + StringBuilder fullName = new StringBuilder(res); + fullName.Append(" ("); + + List nodes = new List(); + var curNode = Parent; + do { + nodes.Add(curNode); + curNode = curNode.Parent; + } while (curNode != null && curNode.FindChild(Path.Combine(curNode.VirtualNodeName, "__init__.py")) != null); + + for (int i = nodes.Count - 1; i >= 0; i--) { + fullName.Append(nodes[i].Caption); + if (i != 0) { + fullName.Append('.'); + } + } + + fullName.Append(")"); + res = fullName.ToString(); + //res += " (" + Parent.Caption + ")"; + } + return res; + } + } + + public override string GetEditLabel() { + return base.Caption; + } + + public override string FileName { + get { + return base.Caption; + } + set { + base.FileName = value; + } + } + + public IProjectEntry GetAnalysis() { + var textBuffer = GetTextBuffer(); + + IProjectEntry analysis; + if (textBuffer != null && textBuffer.TryGetAnalysis(out analysis)) { + return analysis; + } + + return IronPythonToolsPackage.Instance.Analyzer.GetAnalysisFromFile(Url); + } + + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonFolderNode.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonFolderNode.cs new file mode 100644 index 0000000000..7425444565 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonFolderNode.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.IronStudio.Project; +using Microsoft.VisualStudio.Project; +using System.Drawing; +using System.Reflection; +using System.Windows.Forms; +using System.IO; + +namespace Microsoft.IronPythonTools.Project { + /// + /// Node used for a Python package (a directory with __init__ in it). + /// + /// Currently we just provide a specialized icon for the different folder. + /// + class PythonFolderNode : CommonFolderNode { + private ImageList _imageList; + + public PythonFolderNode(CommonProjectNode root, string path, ProjectElement element) + : base(root, path, element) { + } + + public override object GetIconHandle(bool open) { + for (HierarchyNode child = this.FirstChild; child != null; child = child.NextSibling) { + if (child.Url.EndsWith("\\__init__.py")) { + if (_imageList == null) { + _imageList = Utilities.GetImageList(Assembly.GetExecutingAssembly().GetManifestResourceStream("Microsoft.Resources.PythonPackageIcons.bmp")); + } + + return open ? + ((Bitmap)_imageList.Images[0]).GetHicon() : + ((Bitmap)_imageList.Images[1]).GetHicon(); + } + } + if(this.FindChild(Path.Combine(VirtualNodeName, "__init__.py")) != null) { +// if (File.Exists(Path.Combine(VirtualNodeName, "__init__.py"))) { + } + + return base.GetIconHandle(open); + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonGeneralPropertyPage.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonGeneralPropertyPage.cs new file mode 100644 index 0000000000..33fb22d1c5 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonGeneralPropertyPage.cs @@ -0,0 +1,64 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Project; + +namespace Microsoft.IronPythonTools.Project { + [Guid(PythonConstants.GeneralPropertyPageGuid)] + public class PythonGeneralPropertyPage : CommonPropertyPage { + private readonly PythonGeneralyPropertyPageControl _control; + public PythonGeneralPropertyPage() { + _control = new PythonGeneralyPropertyPageControl(this); + } + + public override Control Control { + get { + return _control; + } + } + + public override string Name { + get { return "General"; } + } + + public override void Apply() { + ProjectMgr.SetProjectProperty(CommonConstants.StartupFile, _control.StartupFile); + ProjectMgr.SetProjectProperty(CommonConstants.SearchPath, _control.SearchPaths); + ProjectMgr.SetProjectProperty(CommonConstants.InterpreterPath, _control.InterpreterPath); + ProjectMgr.SetProjectProperty(CommonConstants.WorkingDirectory, _control.WorkingDirectory); + ProjectMgr.SetProjectProperty(CommonConstants.CommandLineArguments, _control.Arguments); + ProjectMgr.SetProjectProperty(CommonConstants.IsWindowsApplication, _control.IsWindowsApplication.ToString()); + ProjectMgr.SetProjectProperty(PythonConstants.DebugStandardLibrary, _control.DebugStandardLibrary.ToString()); + IsDirty = false; + } + + public override void LoadSettings() { + _control.StartupFile = this.ProjectMgr.GetProjectProperty(CommonConstants.StartupFile, false); + _control.SearchPaths = this.ProjectMgr.GetProjectProperty(CommonConstants.SearchPath, false); + _control.InterpreterPath = this.ProjectMgr.GetProjectProperty(CommonConstants.InterpreterPath, false); + _control.WorkingDirectory = this.ProjectMgr.GetProjectProperty(CommonConstants.WorkingDirectory, false); + if (string.IsNullOrEmpty(_control.WorkingDirectory)) { + _control.WorkingDirectory = "."; + } + _control.Arguments = this.ProjectMgr.GetProjectProperty(CommonConstants.CommandLineArguments, false); + _control.IsWindowsApplication = Convert.ToBoolean(this.ProjectMgr.GetProjectProperty(CommonConstants.IsWindowsApplication, false)); + _control.DebugStandardLibrary = Convert.ToBoolean(this.ProjectMgr.GetProjectProperty(PythonConstants.DebugStandardLibrary, false)); + IsDirty = false; + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonGeneralyPropertyPageControl.Designer.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonGeneralyPropertyPageControl.Designer.cs new file mode 100644 index 0000000000..f67b606704 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonGeneralyPropertyPageControl.Designer.cs @@ -0,0 +1,214 @@ +namespace Microsoft.IronPythonTools.Project { + partial class PythonGeneralyPropertyPageControl { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this._applicationGroup = new System.Windows.Forms.GroupBox(); + this._workingDirLabel = new System.Windows.Forms.Label(); + this._workingDirectory = new System.Windows.Forms.TextBox(); + this._windowsApplication = new System.Windows.Forms.CheckBox(); + this._startupFile = new System.Windows.Forms.TextBox(); + this._startupFileLabel = new System.Windows.Forms.Label(); + this._debugGroup = new System.Windows.Forms.GroupBox(); + this._arguments = new System.Windows.Forms.TextBox(); + this._argumentsLabel = new System.Windows.Forms.Label(); + this._searchPaths = new System.Windows.Forms.TextBox(); + this._searchPathLabel = new System.Windows.Forms.Label(); + this._interpreterPath = new System.Windows.Forms.TextBox(); + this._interpreterPathLabel = new System.Windows.Forms.Label(); + this._debugStdLib = new System.Windows.Forms.CheckBox(); + this._applicationGroup.SuspendLayout(); + this._debugGroup.SuspendLayout(); + this.SuspendLayout(); + // + // _applicationGroup + // + this._applicationGroup.Controls.Add(this._workingDirLabel); + this._applicationGroup.Controls.Add(this._workingDirectory); + this._applicationGroup.Controls.Add(this._windowsApplication); + this._applicationGroup.Controls.Add(this._startupFile); + this._applicationGroup.Controls.Add(this._startupFileLabel); + this._applicationGroup.Location = new System.Drawing.Point(4, 4); + this._applicationGroup.Name = "_applicationGroup"; + this._applicationGroup.Size = new System.Drawing.Size(437, 112); + this._applicationGroup.TabIndex = 0; + this._applicationGroup.TabStop = false; + this._applicationGroup.Text = "Application"; + // + // _workingDirLabel + // + this._workingDirLabel.AutoSize = true; + this._workingDirLabel.Location = new System.Drawing.Point(4, 54); + this._workingDirLabel.Name = "_workingDirLabel"; + this._workingDirLabel.Size = new System.Drawing.Size(92, 13); + this._workingDirLabel.TabIndex = 10; + this._workingDirLabel.Text = "Working Directory"; + // + // _workingDirectory + // + this._workingDirectory.Location = new System.Drawing.Point(139, 51); + this._workingDirectory.Name = "_workingDirectory"; + this._workingDirectory.Size = new System.Drawing.Size(286, 20); + this._workingDirectory.TabIndex = 1; + this._workingDirectory.TextChanged += new System.EventHandler(this.Changed); + // + // _windowsApplication + // + this._windowsApplication.AutoSize = true; + this._windowsApplication.Location = new System.Drawing.Point(7, 79); + this._windowsApplication.Name = "_windowsApplication"; + this._windowsApplication.Size = new System.Drawing.Size(125, 17); + this._windowsApplication.TabIndex = 2; + this._windowsApplication.Text = "Windows Application"; + this._windowsApplication.UseVisualStyleBackColor = true; + this._windowsApplication.CheckedChanged += new System.EventHandler(this.Changed); + // + // _startupFile + // + this._startupFile.Location = new System.Drawing.Point(139, 25); + this._startupFile.Name = "_startupFile"; + this._startupFile.Size = new System.Drawing.Size(286, 20); + this._startupFile.TabIndex = 0; + this._startupFile.TextChanged += new System.EventHandler(this.Changed); + // + // _startupFileLabel + // + this._startupFileLabel.AutoSize = true; + this._startupFileLabel.Location = new System.Drawing.Point(4, 28); + this._startupFileLabel.Name = "_startupFileLabel"; + this._startupFileLabel.Size = new System.Drawing.Size(60, 13); + this._startupFileLabel.TabIndex = 0; + this._startupFileLabel.Text = "Startup File"; + // + // _debugGroup + // + this._debugGroup.Controls.Add(this._debugStdLib); + this._debugGroup.Controls.Add(this._arguments); + this._debugGroup.Controls.Add(this._argumentsLabel); + this._debugGroup.Controls.Add(this._searchPaths); + this._debugGroup.Controls.Add(this._searchPathLabel); + this._debugGroup.Controls.Add(this._interpreterPath); + this._debugGroup.Controls.Add(this._interpreterPathLabel); + this._debugGroup.Location = new System.Drawing.Point(4, 122); + this._debugGroup.Name = "_debugGroup"; + this._debugGroup.Size = new System.Drawing.Size(437, 131); + this._debugGroup.TabIndex = 15; + this._debugGroup.TabStop = false; + this._debugGroup.Text = "Debug"; + // + // _arguments + // + this._arguments.Location = new System.Drawing.Point(139, 48); + this._arguments.Name = "_arguments"; + this._arguments.Size = new System.Drawing.Size(286, 20); + this._arguments.TabIndex = 1; + this._arguments.TextChanged += new System.EventHandler(this.Changed); + // + // _argumentsLabel + // + this._argumentsLabel.AutoSize = true; + this._argumentsLabel.Location = new System.Drawing.Point(4, 51); + this._argumentsLabel.Name = "_argumentsLabel"; + this._argumentsLabel.Size = new System.Drawing.Size(130, 13); + this._argumentsLabel.TabIndex = 19; + this._argumentsLabel.Text = "Command Line Arguments"; + // + // _searchPaths + // + this._searchPaths.Location = new System.Drawing.Point(139, 22); + this._searchPaths.Name = "_searchPaths"; + this._searchPaths.Size = new System.Drawing.Size(286, 20); + this._searchPaths.TabIndex = 0; + this._searchPaths.TextChanged += new System.EventHandler(this.Changed); + // + // _searchPathLabel + // + this._searchPathLabel.AutoSize = true; + this._searchPathLabel.Location = new System.Drawing.Point(4, 25); + this._searchPathLabel.Name = "_searchPathLabel"; + this._searchPathLabel.Size = new System.Drawing.Size(71, 13); + this._searchPathLabel.TabIndex = 17; + this._searchPathLabel.Text = "Search Paths"; + // + // _interpreterPath + // + this._interpreterPath.Location = new System.Drawing.Point(139, 74); + this._interpreterPath.Name = "_interpreterPath"; + this._interpreterPath.Size = new System.Drawing.Size(286, 20); + this._interpreterPath.TabIndex = 2; + this._interpreterPath.TextChanged += new System.EventHandler(this.Changed); + // + // _interpreterPathLabel + // + this._interpreterPathLabel.AutoSize = true; + this._interpreterPathLabel.Location = new System.Drawing.Point(4, 77); + this._interpreterPathLabel.Name = "_interpreterPathLabel"; + this._interpreterPathLabel.Size = new System.Drawing.Size(80, 13); + this._interpreterPathLabel.TabIndex = 15; + this._interpreterPathLabel.Text = "Interpreter Path"; + // + // _debugStdLib + // + this._debugStdLib.AutoSize = true; + this._debugStdLib.Location = new System.Drawing.Point(7, 98); + this._debugStdLib.Name = "_debugStdLib"; + this._debugStdLib.Size = new System.Drawing.Size(138, 17); + this._debugStdLib.TabIndex = 20; + this._debugStdLib.Text = "Debug Standard Library"; + this._debugStdLib.UseVisualStyleBackColor = true; + this._debugStdLib.CheckedChanged += Changed; + // + // PythonGeneralyPropertyPageControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this._debugGroup); + this.Controls.Add(this._applicationGroup); + this.Name = "PythonGeneralyPropertyPageControl"; + this.Size = new System.Drawing.Size(457, 285); + this._applicationGroup.ResumeLayout(false); + this._applicationGroup.PerformLayout(); + this._debugGroup.ResumeLayout(false); + this._debugGroup.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.GroupBox _applicationGroup; + private System.Windows.Forms.Label _startupFileLabel; + private System.Windows.Forms.TextBox _startupFile; + private System.Windows.Forms.CheckBox _windowsApplication; + private System.Windows.Forms.TextBox _workingDirectory; + private System.Windows.Forms.Label _workingDirLabel; + private System.Windows.Forms.GroupBox _debugGroup; + private System.Windows.Forms.TextBox _arguments; + private System.Windows.Forms.Label _argumentsLabel; + private System.Windows.Forms.TextBox _searchPaths; + private System.Windows.Forms.Label _searchPathLabel; + private System.Windows.Forms.TextBox _interpreterPath; + private System.Windows.Forms.Label _interpreterPathLabel; + private System.Windows.Forms.CheckBox _debugStdLib; + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonGeneralyPropertyPageControl.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonGeneralyPropertyPageControl.cs new file mode 100644 index 0000000000..487d19c46c --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonGeneralyPropertyPageControl.cs @@ -0,0 +1,76 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Drawing; +using System.Data; +using System.Linq; +using System.Text; +using System.Windows.Forms; + +namespace Microsoft.IronPythonTools.Project { + public partial class PythonGeneralyPropertyPageControl : UserControl { + private readonly PythonGeneralPropertyPage _propPage; + + public PythonGeneralyPropertyPageControl() { + InitializeComponent(); + } + + internal PythonGeneralyPropertyPageControl(PythonGeneralPropertyPage newPythonGeneralPropertyPage) + : this() { + _propPage = newPythonGeneralPropertyPage; + } + + public string StartupFile { + get { return _startupFile.Text; } + set { _startupFile.Text = value; } + } + + public string WorkingDirectory { + get { return _workingDirectory.Text; } + set { _workingDirectory.Text = value; } + } + + public bool IsWindowsApplication { + get { return _windowsApplication.Checked; } + set { _windowsApplication.Checked = value; } + } + + public string SearchPaths { + get { return _searchPaths.Text; } + set { _searchPaths.Text = value; } + } + + public string Arguments { + get { return _arguments.Text; } + set { _arguments.Text = value; } + } + + public string InterpreterPath { + get { return _interpreterPath.Text; } + set { _interpreterPath.Text = value; } + } + + public bool DebugStandardLibrary { + get { return _debugStdLib.Checked; } + set { _debugStdLib.Checked = value; } + } + + private void Changed(object sender, EventArgs e) { + _propPage.IsDirty = true; + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonGeneralyPropertyPageControl.resx b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonGeneralyPropertyPageControl.resx new file mode 100644 index 0000000000..29dcb1b3a3 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonGeneralyPropertyPageControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonNonCodeFileNode.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonNonCodeFileNode.cs new file mode 100644 index 0000000000..2aee07ba8a --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonNonCodeFileNode.cs @@ -0,0 +1,56 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.IronStudio.Project; +using Microsoft.VisualStudio.Project; +using Microsoft.Windows.Design.Host; +using Microsoft.IronPythonTools.Designer; + +namespace Microsoft.IronPythonTools.Project { + class PythonNonCodeFileNode : CommonNonCodeFileNode { + private DesignerContext _designerContext; + + public PythonNonCodeFileNode(CommonProjectNode root, ProjectElement e) + : base(root, e) { + } + + protected internal Microsoft.Windows.Design.Host.DesignerContext DesignerContext { + get { + if (_designerContext == null) { + _designerContext = new DesignerContext(); + //Set the EventBindingProvider for this XAML file so the designer will call it + //when event handlers need to be generated + _designerContext.EventBindingProvider = new WpfEventBindingProvider(this.Parent.FindChild(this.Url.Replace(".xaml", ".py")) as PythonFileNode); + } + return _designerContext; + } + } + + protected override object CreateServices(Type serviceType) { + object service = null; + if (typeof(DesignerContext) == serviceType) { + service = this.DesignerContext; + } else { + return base.CreateServices(serviceType); + } + return service; + } + + + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonProjectFactory.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonProjectFactory.cs new file mode 100644 index 0000000000..3a35433bfc --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonProjectFactory.cs @@ -0,0 +1,37 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.Project; +using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; + +namespace Microsoft.IronPythonTools.Project { + /// + /// Creates Python Projects + /// + [Guid(PythonConstants.ProjectFactoryGuid)] + public class PythonProjectFactory : ProjectFactory { + + public PythonProjectFactory(PythonProjectPackage/*!*/ package) + : base(package) { + } + + protected override ProjectNode/*!*/ CreateProject() { + PythonProjectNode project = new PythonProjectNode((PythonProjectPackage)Package); + project.SetSite((IOleServiceProvider)((IServiceProvider)Package).GetService(typeof(IOleServiceProvider))); + return project; + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonProjectNode.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonProjectNode.cs new file mode 100644 index 0000000000..623a19acd2 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonProjectNode.cs @@ -0,0 +1,93 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using Microsoft.IronPythonTools.Designer; +using Microsoft.IronPythonTools.Navigation; +using Microsoft.IronStudio.Project; +using Microsoft.VisualStudio.Project; +using Microsoft.Windows.Design.Host; + +namespace Microsoft.IronPythonTools.Project { + [Guid(PythonConstants.ProjectNodeGuid)] + public class PythonProjectNode : CommonProjectNode { + private DesignerContext _designerContext; + public PythonProjectNode(CommonProjectPackage package) + : base(package, Utilities.GetImageList(typeof(PythonProjectNode).Assembly.GetManifestResourceStream(PythonConstants.ProjectImageList))) { + } + + public override CommonFileNode CreateCodeFileNode(ProjectElement item) { + return new PythonFileNode(this, item); + } + + public override CommonFileNode CreateNonCodeFileNode(ProjectElement item) { + return new PythonNonCodeFileNode(this, item); + } + + public override Type GetProjectFactoryType() { + return typeof(PythonProjectFactory); + } + + public override string GetProjectName() { + return "PythonProject"; + } + + public override string GetCodeFileExtension() { + return PythonConstants.FileExtension; + } + + public override string GetFormatList() { + return String.Format(CultureInfo.CurrentCulture, ".py"/*Resources.ProjectFileExtensionFilter*/, "\0", "\0"); + } + + public override Type GetGeneralPropertyPageType() { + return typeof(PythonGeneralPropertyPage); + } + + public override Type GetEditorFactoryType() { + return typeof(PythonEditorFactory); + } + + public override Type GetLibraryManagerType() { + return typeof(IPythonLibraryManager); + } + + public override string GetProjectFileExtension() { + return ".pyproj"; + } + + protected internal override FolderNode CreateFolderNode(string path, ProjectElement element) { + return new PythonFolderNode(this, path, element); + } + + protected override internal Microsoft.Windows.Design.Host.DesignerContext DesignerContext { + get { + if (_designerContext == null) { + _designerContext = new DesignerContext(); + //Set the RuntimeNameProvider so the XAML designer will call it when items are added to + //a design surface. Since the provider does not depend on an item context, we provide it at + //the project level. + // This is currently disabled because we don't successfully serialize to the remote domain + // and the default name provider seems to work fine. Likely installing our assembly into + // the GAC or implementing an IsolationProvider would solve this. + //designerContext.RuntimeNameProvider = new PythonRuntimeNameProvider(); + } + return _designerContext; + } + } + + } +} \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonProjectPackage.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonProjectPackage.cs new file mode 100644 index 0000000000..587cd2de62 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonProjectPackage.cs @@ -0,0 +1,98 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using Microsoft.IronPythonTools.Navigation; +using Microsoft.IronStudio.Project; +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio.Shell; + +namespace Microsoft.IronPythonTools.Project { + //Set the projectsTemplatesDirectory to a non-existant path to prevent VS from including the working directory as a valid template path + [PackageRegistration(UseManagedResourcesOnly = true)] + [ProvideProjectFactory(typeof(PythonProjectFactory), PythonConstants.LanguageName, "IronPython Project Files (*.pyproj);*.pyproj", "pyproj", "pyproj", ".\\NullPath", LanguageVsTemplate = PythonConstants.LanguageName)] + //[SingleFileGeneratorSupportRegistration(typeof(PythonProjectFactory))] + [ProvideObject(typeof(PythonGeneralPropertyPage))] + + [ProvideEditorExtension(typeof(PythonEditorFactory), ".py", 32)] + [ProvideEditorLogicalView(typeof(PythonEditorFactory), "{7651a703-06e5-11d1-8ebd-00a0c90f26ea}")] //LOGVIEWID_TextView + [ProvideEditorLogicalView(typeof(PythonEditorFactory), "{7651a702-06e5-11d1-8ebd-00a0c90f26ea}")] //LOGVIEWID_Designer + [ProvideEditorLogicalView(typeof(PythonEditorFactory), "{7651a701-06e5-11d1-8ebd-00a0c90f26ea}")] //LOGVIEWID_Code + /*[ProvideLoadKey(CommonConstants.PLKMinEdition, PythonProjectConstants.PLKProductVersion, + PythonProjectConstants.PLKProductName, CommonConstants.PLKCompanyName, CommonConstants.PLKResourceID)]*/ + [Guid(PythonConstants.ProjectSystemPackageGuid)] + //[WebSiteProject(PythonProjectConstants.IPyLanguageName, PythonProjectConstants.IPyLanguageName)] + [ProvideService(typeof(IPythonStarter))] + + //[ProvideAutoLoad("ADFC4E64-0397-11D1-9F4E-00A0C911004F")] // load w/ and w/o a solution so we can initialize our filetypes + //[ProvideAutoLoad("f1536ef8-92ec-443c-9ed7-fdadf150da82")] + public class PythonProjectPackage : CommonProjectPackage { + public override ProjectFactory CreateProjectFactory() { + return new PythonProjectFactory(this); + } + + public override CommonEditorFactory CreateEditorFactory() { + return new PythonEditorFactory(this); + } + + /// + /// This method is called to get the icon that will be displayed in the + /// Help About dialog when this package is selected. + /// + /// The resource id corresponding to the icon to display on the Help About dialog + public override uint GetIconIdForAboutBox() { + return PythonConstants.IconIdForAboutBox; + } + /// + /// This method is called during Devenv /Setup to get the bitmap to + /// display on the splash screen for this package. + /// + /// The resource id corresponding to the bitmap to display on the splash screen + public override uint GetIconIdForSplashScreen() { + return PythonConstants.IconIfForSplashScreen; + } + /// + /// This methods provides the product official name, it will be + /// displayed in the help about dialog. + /// + public override string GetProductName() { + return PythonConstants.LanguageName; + } + + /// + /// This methods provides the product description, it will be + /// displayed in the help about dialog. + /// + public override string GetProductDescription() { + return "IronPython"; + //return Resources.ProductDescription; + } + /// + /// This methods provides the product version, it will be + /// displayed in the help about dialog. + /// + public override string GetProductVersion() { + return this.GetType().Assembly.GetName().Version.ToString(); + } + + /// + /// Creates an instance of a language specific service that + /// allows to start a project or a file with or without debugging. + /// + protected override IStarter/*!*/ CreateStarter() { + return new PythonStarter((IServiceProvider)this); + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonStarter.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonStarter.cs new file mode 100644 index 0000000000..69f955b08c --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Project/PythonStarter.cs @@ -0,0 +1,135 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Specialized; +using System.IO; +using System.Reflection; +using Microsoft.IronPythonTools.Navigation; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Project; +using Microsoft.Win32; + +namespace Microsoft.IronPythonTools.Project { + + /// + /// Provides Python specific functionality for starting a project + /// or a file with or without debugging. + /// + public class PythonStarter : CommonStarter, IPythonStarter { + private static string _ipyExe, _ipywExe; + + public PythonStarter(IServiceProvider serviceProvider) : base(serviceProvider) {} + + public override string InstallPath { + get { + return PythonRuntimeHost.GetPythonInstallDir() ?? Path.GetDirectoryName(typeof(PythonStarter).Assembly.Location); + } + } + + public override string ChironPath { + get { + string result; +#if DEBUG + result = Environment.GetEnvironmentVariable("DLR_ROOT"); + if (result != null) { + result = Path.Combine(result, @"Bin\Silveright4Debug\Chiron.exe"); + if (File.Exists(result)) { + return result; + } + } +#endif + result = PythonRuntimeHost.GetPythonInstallDir(); + if (result != null) { + result = Path.Combine(result, @"Silverlight\bin\Chiron.exe"); + if (File.Exists(result)) { + return result; + } + } + + return base.ChironPath; + } + } + + private string InterpreterExecutable { + get { + if (_ipyExe == null) { + //ipy.exe is installed along with package assembly + _ipyExe = Path.Combine( + InstallPath, + "ipy.exe"); + } + return _ipyExe; + } + } + + private string WindowsInterpreterExecutable { + get { + if (_ipywExe == null) { + //ipy.exe is installed along with package assembly + _ipywExe = Path.Combine( + InstallPath, + "ipyw.exe"); + } + return _ipywExe; + } + } + + protected override void SetupEnvironment(CommonProjectNode project, StringDictionary environment) { + if (project != null) { + //IronPython passes search path via IRONPYTHONPATH environment variable + string searchPath = project.GetProjectProperty(CommonConstants.SearchPath, true); + if (!string.IsNullOrEmpty(searchPath)) { + environment[PythonConstants.IronPythonPath] = searchPath; + } + } + } + + public override string CreateCommandLineNoDebug(CommonProjectNode project, string startupFile) { + string cmdLineArgs = null; + if (project != null) { + cmdLineArgs = project.GetProjectProperty(CommonConstants.CommandLineArguments, true); + } + + return String.Format("{0} \"{1}\" {2}", GetOptions(project), startupFile, cmdLineArgs); + } + + public override string GetInterpreterExecutable(CommonProjectNode project) { + bool isWindows = Convert.ToBoolean(project.GetProjectProperty(CommonConstants.IsWindowsApplication, true)); + return isWindows ? WindowsInterpreterExecutable : InterpreterExecutable; + } + + public override string CreateCommandLineDebug(CommonProjectNode project, string startupFile) { + string cmdLineArgs = null; + if (project != null) { + cmdLineArgs = project.GetProjectProperty(CommonConstants.CommandLineArguments, true); + } + return String.Format("-X:Debug {0} \"{1}\" {2}", GetOptions(project), startupFile, cmdLineArgs); + } + + private string GetOptions(CommonProjectNode project) { + if (project != null) { + var debugStdLib = project.GetProjectProperty(PythonConstants.DebugStandardLibrary, false); + bool debugStdLibResult; + if (!bool.TryParse(debugStdLib, out debugStdLibResult) || !debugStdLibResult) { + var res = "-X:NoDebug \"" + System.Text.RegularExpressions.Regex.Escape(Path.Combine(InstallPath, "Lib\\")) + ".*\""; + + return res; + } + } + + return ""; + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Repl/PythonVsEvaluator.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Repl/PythonVsEvaluator.cs new file mode 100644 index 0000000000..4387f0217e --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Repl/PythonVsEvaluator.cs @@ -0,0 +1,40 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.IronPythonTools.Library.Repl; +using Microsoft.IronPythonTools.Options; +using Microsoft.Scripting.Hosting; +using Microsoft.IronStudio.Core.Repl; + +namespace Microsoft.IronPythonTools.Repl { + internal class PythonVsEvaluator : PythonEvaluator { + // Constructed via reflection when deserialized from the registry. + public PythonVsEvaluator() { + } + + public override void PublishScopeVariables(ScriptScope scope) { + scope.SetVariable("DTE", IronPythonToolsPackage.Instance.DTE); + } + + protected override bool ShouldEvaluateForCompletion(string source) { + switch (IronPythonToolsPackage.Instance.OptionsPage.ReplIntellisenseMode) { + case ReplIntellisenseMode.AlwaysEvaluate: return true; + case ReplIntellisenseMode.DontEvaluateCalls: return base.ShouldEvaluateForCompletion(source); + case ReplIntellisenseMode.NeverEvaluate: return false; + default: throw new InvalidOperationException(); + } + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/Repl/RemotePythonVsEvaluator.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/Repl/RemotePythonVsEvaluator.cs new file mode 100644 index 0000000000..1dc3baba71 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/Repl/RemotePythonVsEvaluator.cs @@ -0,0 +1,71 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.IronPythonTools.Intellisense; +using Microsoft.IronPythonTools.Language; +using Microsoft.IronPythonTools.Library.Repl; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Core.Repl; +using Microsoft.IronStudio.Repl; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.IronPythonTools.Repl { + public sealed class RemotePythonVsEvaluator : RemotePythonEvaluator { + // Constructed via reflection when deserialized from the registry. + public RemotePythonVsEvaluator() { + } + + public override void TextViewCreated(IReplWindow window, VisualStudio.Text.Editor.ITextView view) { + var adapterFactory = IronPythonToolsPackage.ComponentModel.GetService(); + new EditFilter(IronPythonToolsPackage.ComponentModel.GetService(), (IWpfTextView)view, adapterFactory.GetViewAdapter(view)); + window.UseSmartUpDown = IronPythonToolsPackage.Instance.OptionsPage.ReplSmartHistory; + base.TextViewCreated(window, view); + } + + public override void Reset() { + base.Reset(); + Initialize(); + } + + public override Dictionary GetOptions() { + Dictionary res = new Dictionary(); + foreach (string option in IronPythonToolsPackage.Instance.OptionsPage.InteractiveOptions.Split(';')) { + string[] nameValue = option.Split(new[] { '=' }, 2); + if (nameValue.Length == 2) { + res[nameValue[0]] = nameValue[1]; + } + } + return res; + } + + public void Initialize() { + string filename, dir; + if (CommonPackage.TryGetStartupFileAndDirectory(out filename, out dir)) { + RemoteScriptFactory.SetCurrentDirectory(dir); + } + } + + protected override bool ShouldEvaluateForCompletion(string source) { + switch (IronPythonToolsPackage.Instance.OptionsPage.ReplIntellisenseMode) { + case ReplIntellisenseMode.AlwaysEvaluate: return true; + case ReplIntellisenseMode.DontEvaluateCalls: return base.ShouldEvaluateForCompletion(source); + case ReplIntellisenseMode.NeverEvaluate: return false; + default: throw new InvalidOperationException(); + } + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonTools/SolutionAdvisor.cs b/Tools/IronStudio/IronPythonTools/IronPythonTools/SolutionAdvisor.cs new file mode 100644 index 0000000000..4542b1fb2e --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonTools/SolutionAdvisor.cs @@ -0,0 +1,78 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio; + +namespace Microsoft.IronPythonTools { + class SolutionAdvisor : IVsSolutionEvents { + private int _pyProjectCount = 0; + + public int OnAfterCloseSolution(object pUnkReserved) { + return VSConstants.S_OK; + } + + public int OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) { + return VSConstants.S_OK; + } + + public int OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) { + var project = pHierarchy.GetProject().GetPythonProject(); + if (project != null) { + if (_pyProjectCount++ == 0) { + IronPythonToolsPackage.Instance.Analyzer.ImplicitProject = false; + } + } + return VSConstants.S_OK; + } + + public int OnAfterOpenSolution(object pUnkReserved, int fNewSolution) { + return VSConstants.S_OK; + } + + public int OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) { + var project = pHierarchy.GetProject().GetPythonProject(); + if (project != null) { + if (--_pyProjectCount == 0) { + IronPythonToolsPackage.Instance.Analyzer.ImplicitProject = true; + } + } + return VSConstants.S_OK; + } + + public int OnBeforeCloseSolution(object pUnkReserved) { + return VSConstants.S_OK; + } + + public int OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) { + return VSConstants.S_OK; + } + + public int OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel) { + return VSConstants.S_OK; + } + + public int OnQueryCloseSolution(object pUnkReserved, ref int pfCancel) { + return VSConstants.S_OK; + } + + public int OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel) { + return VSConstants.S_OK; + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/IronPythonToolsPackage.cs b/Tools/IronStudio/IronPythonTools/IronPythonToolsPackage.cs new file mode 100644 index 0000000000..8c7cc0317c --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/IronPythonToolsPackage.cs @@ -0,0 +1,243 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using Microsoft.IronPythonTools.Editor; +using Microsoft.IronPythonTools.Intellisense; +using Microsoft.IronPythonTools.Navigation; +using Microsoft.IronPythonTools.Options; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Navigation; +using Microsoft.Scripting.Hosting; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TextManager.Interop; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronPythonTools { + /// + /// This is the class that implements the package exposed by this assembly. + /// + /// The minimum requirement for a class to be considered a valid package for Visual Studio + /// is to implement the IVsPackage interface and register itself with the shell. + /// This package uses the helper classes defined inside the Managed Package Framework (MPF) + /// to do it: it derives from the Package class that provides the implementation of the + /// IVsPackage interface and uses the registration attributes defined in the framework to + /// register itself and its components with the shell. + /// + [PackageRegistration(UseManagedResourcesOnly = true)] // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is a package. + [InstalledProductRegistration("#110", "#112", "1.0", // This attribute is used to register the informations needed to show the this package in the Help/About dialog of Visual Studio. + IconResourceID = 400)] + [ProvideMenuResource(1000, 1)] // This attribute is needed to let the shell know that this package exposes some menus. + [ProvideAutoLoad(CommonConstants.UIContextNoSolution)] + [ProvideAutoLoad(CommonConstants.UIContextSolutionExists)] + [ProvideLanguageEditorOptionPage(typeof(PythonOptionsPage), PythonConstants.LanguageName, "Advanced", "", "113")] + [ProvideLanguageEditorOptionPage(typeof(PythonIntellisenseOptionsPage), PythonConstants.LanguageName, "Intellisense", "", "113")] + [Guid(GuidList.guidIronPythonToolsPkgString)] // our packages GUID + [ProvideLanguageService(typeof(PythonLanguageInfo), PythonConstants.LanguageName, 106, RequestStockColors = true, ShowSmartIndent=true, ShowCompletion=true, DefaultToInsertSpaces=true, HideAdvancedMembersByDefault=false, EnableAdvancedMembersOption=true, ShowDropDownOptions=true)] + [ProvideLanguageExtension(typeof(PythonLanguageInfo), ".py")] + public sealed class IronPythonToolsPackage : CommonPackage { + private LanguagePreferences _langPrefs; + public static IronPythonToolsPackage Instance; + + /// + /// Default constructor of the package. + /// Inside this method you can place any initialization code that does not require + /// any Visual Studio service because at this point the package object is created but + /// not sited yet inside Visual Studio environment. The place to do all the other + /// initialization is the Initialize method. + /// + public IronPythonToolsPackage() { + Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString())); + Instance = this; + + LoadAssemblies(); + } + + /// + /// VS seems to load extensions via Assembly.LoadFrom. When an assembly is being loaded via Assembly.Load the CLR fusion probes privatePath + /// set in App.config (devenv.exe.config) first and then tries the code base of the assembly that called Assembly.Load if it was itself loaded via LoadFrom. + /// In order to locate IronPython.Modules correctly, the call to Assembly.Load must originate from an assembly in IronPythonTools installation folder. + /// Although Microsoft.Scripting is also in that folder it can be loaded first by IronRuby and that causes the Assembly.Load to search in IronRuby's + /// installation folder. Adding a reference to IronPython.Modules also makes sure that the assembly is loaded from the same location as IronPythonToolsCore. + /// + private static void LoadAssemblies() { + GC.KeepAlive(typeof(IronPython.Modules.ArrayModule)); // IronPython.Modules + } + + internal static void NavigateTo(string view, Guid docViewGuidType, int line, int col) { + IVsTextManager textMgr = (IVsTextManager)Instance.GetService(typeof(SVsTextManager)); + var model = Instance.GetService(typeof(SComponentModel)) as IComponentModel; + var adapter = model.GetService(); + + IVsTextView viewAdapter; + IVsUIShellOpenDocument uiShellOpenDocument = Instance.GetService(typeof(SVsUIShellOpenDocument)) as IVsUIShellOpenDocument; + IVsUIHierarchy hierarchy; + uint itemid; + IVsWindowFrame pWindowFrame; + + VsShellUtilities.OpenDocument( + Instance, + view, + Guid.Empty, + out hierarchy, + out itemid, + out pWindowFrame, + out viewAdapter); + + ErrorHandler.ThrowOnFailure(pWindowFrame.Show()); + + // Set the cursor at the beginning of the declaration. + ErrorHandler.ThrowOnFailure(viewAdapter.SetCaretPos(line, col)); + // Make sure that the text is visible. + viewAdapter.CenterLines(line, 1); + } + + public override ScriptEngine CreateScriptEngine() { + return RuntimeHost.ScriptEngine; + } + + public override Type GetLibraryManagerType() { + return typeof(IPythonLibraryManager); + } + + internal PythonOptionsPage OptionsPage { + get { + return (PythonOptionsPage)GetDialogPage(typeof(PythonOptionsPage)); + } + } + + internal PythonIntellisenseOptionsPage IntellisenseOptionsPage { + get { + return (PythonIntellisenseOptionsPage)GetDialogPage(typeof(PythonIntellisenseOptionsPage)); + } + } + + private IPythonAnalyzer _analyzer; + + internal IPythonAnalyzer Analyzer { + get { + if (_analyzer == null) { + var model = GetService(typeof(SComponentModel)) as IComponentModel; + _analyzer = model.GetService(); + //_analyzer = new PythonAnalyzer(model); + } + return _analyzer; + } + } + + public override LibraryManager CreateLibraryManager(CommonPackage package) { + return new PythonLibraryManager((IronPythonToolsPackage)package); + } + + public IVsSolution Solution { + get { + return GetService(typeof(SVsSolution)) as IVsSolution; + } + } + + public IPythonRuntimeHost RuntimeHost { + get { + return ComponentModel.GetService(); + } + } + + ///////////////////////////////////////////////////////////////////////////// + // Overriden Package Implementation + + /// + /// Initialization of the package; this method is called right after the package is sited, so this is the place + /// where you can put all the initilaization code that rely on services provided by VisualStudio. + /// + protected override void Initialize() { + Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString())); + base.Initialize(); + + // Add our command handlers for menu (commands must exist in the .vsct file) + OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService; + if (null != mcs) { + foreach (var command in Commands.CommandTable.Commands) { + var beforeQueryStatus = command.BeforeQueryStatus; + CommandID toolwndCommandID = new CommandID(GuidList.guidIronPythonToolsCmdSet, command.CommandId); + if (beforeQueryStatus == null) { + MenuCommand menuToolWin = new MenuCommand(command.DoCommand, toolwndCommandID); + mcs.AddCommand(menuToolWin); + } else { + OleMenuCommand menuToolWin = new OleMenuCommand(command.DoCommand, toolwndCommandID); + menuToolWin.BeforeQueryStatus += beforeQueryStatus; + mcs.AddCommand(menuToolWin); + } + } + } + + // This is just to force the MEF creation of the DLR runtime hosting layer, including the content type and runtime. + if (RuntimeHost == null) { + throw new InvalidOperationException("Unable to obtain the DLR Runtime hosting component"); + } + + RuntimeHost.EnterOutliningModeOnOpen = OptionsPage.EnterOutliningModeOnOpen; + + // register our language service so that we can support features like the navigation bar + var langService = new PythonLanguageInfo(this); + ((IServiceContainer)this).AddService(langService.GetType(), langService, true); + + var solution = (IVsSolution)Package.GetGlobalService(typeof(SVsSolution)); + uint cookie; + ErrorHandler.ThrowOnFailure(solution.AdviseSolutionEvents(new SolutionAdvisor(), out cookie)); + + IVsTextManager textMgr = (IVsTextManager)Instance.GetService(typeof(SVsTextManager)); + var langPrefs = new LANGPREFERENCES[1]; + langPrefs[0].guidLang = typeof(PythonLanguageInfo).GUID; + ErrorHandler.ThrowOnFailure(textMgr.GetUserPreferences(null, null, langPrefs, null)); + _langPrefs = new LanguagePreferences(langPrefs[0]); + + Guid guid = typeof(IVsTextManagerEvents2).GUID; + IConnectionPoint connectionPoint; + ((IConnectionPointContainer)textMgr).FindConnectionPoint(ref guid, out connectionPoint); + connectionPoint.Advise(_langPrefs, out cookie); + + // propagate options to our runtime host + RuntimeHost.IntersectMembers = IntellisenseOptionsPage.IntersectMembers; + RuntimeHost.EnterOutliningModeOnOpen = OptionsPage.EnterOutliningModeOnOpen; + RuntimeHost.HideAdvancedMembers = Instance.LangPrefs.HideAdvancedMembers; + } + + internal LanguagePreferences LangPrefs { + get { + return _langPrefs; + } + } + + public EnvDTE.DTE DTE { + get { + return (EnvDTE.DTE)GetService(typeof(EnvDTE.DTE)); + } + } + + public IContentType ContentType { + get { + return RuntimeHost.ContentType; + } + } + + } +} diff --git a/Tools/IronStudio/IronPythonTools/Overview.txt b/Tools/IronStudio/IronPythonTools/Overview.txt new file mode 100644 index 0000000000..818e408c4c --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Overview.txt @@ -0,0 +1,47 @@ +Namespace Overview: + + Scripting\ + Things that logically be long in Microsoft.Scripting (e.g. pure DLR utils) + + PyAnalysis\ + IronPython analysis + + Interpreter\ + Core interpreter logic for driving interpretation of the AST + Values\ + Implementation of various abstract objects which we can perform operations on. + + VisualStudio\ + MPFProj implementation + + IronStudio\ + All common base infrastructure sharable by multiple languages + Includes misc helpers such as DlrUtils namespace, VsExtensions class + + Navigation\ + Contains everything related to object browser support and navigating around code + Project\ + Includes everything related to the project system + Repl\ + Common REPL implementation + + IronPythonTools\ + All IronPython specific VS code: + Intellisense\ + Includes everything related to intellisense (collapses in Analysis sub directory) + Navigation\ + Includes everything related to code navigation features such as the object browser. + Project\ + Includes everything related to the project system + Commands\ + Includes implementation of IronPython commands such as displaying the REPL window, + showing text in the REPL, etc... + Templates\ + Includes templates for items (single files) and projects. To add a new template you need + to: + create a corresponding .vstemplate file w/ associated content and put it into a ZIP file + add the ZIP file to the project + set the ZIP file to have a Build Action of type Content and set Include in VSIX true + Add the ZIP files directory to the .vsixmanifest file + + \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/PkgCmdID.cs b/Tools/IronStudio/IronPythonTools/PkgCmdID.cs new file mode 100644 index 0000000000..df6107ffa4 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/PkgCmdID.cs @@ -0,0 +1,30 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +// PkgCmdID.cs +// MUST match PkgCmdID.h +using System; + +namespace Microsoft.IronPythonTools +{ + static class PkgCmdIDList + { + + public const uint cmdidReplWindow = 0x101; + public const uint cmdidExecuteFileInRepl = 0x102; + public const uint cmdidSendToRepl = 0x103; + public const uint cmdidSendToDefiningModule = 0x104; + public const uint cmdidFillParagraph = 0x105; + }; +} \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/Properties/AssemblyInfo.cs b/Tools/IronStudio/IronPythonTools/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..1ac1aacdf5 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Properties/AssemblyInfo.cs @@ -0,0 +1,42 @@ +using System; +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("IronPythonTools")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("IronPythonTools")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] +[assembly: CLSCompliant(false)] +[assembly: NeutralResourcesLanguage("en-US")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +#if MS_SIGNED +[assembly: InternalsVisibleTo("AnalysisTest, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +#else +[assembly: InternalsVisibleTo("AnalysisTest, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c10ce00dd2e0ce5046d68183d3ad035b47e92bf0ce7bcf8a03a217ca5d0b0c7db973fdf97579b52b502a23d4069dbf043389e1ab65a1d6c508a9837f3e2350f15e05cc63c0fc4b0410867a51919090e4c33f80203e9b0035b21c32bae20f98b068f90d99a50133a5336480d94039b176519f5fd8524765f33be43da65c4b68ba")] +[assembly: InternalsVisibleTo("UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c10ce00dd2e0ce5046d68183d3ad035b47e92bf0ce7bcf8a03a217ca5d0b0c7db973fdf97579b52b502a23d4069dbf043389e1ab65a1d6c508a9837f3e2350f15e05cc63c0fc4b0410867a51919090e4c33f80203e9b0035b21c32bae20f98b068f90d99a50133a5336480d94039b176519f5fd8524765f33be43da65c4b68ba")] +#endif + + diff --git a/Tools/IronStudio/IronPythonTools/PythonConstants.cs b/Tools/IronStudio/IronPythonTools/PythonConstants.cs new file mode 100644 index 0000000000..60082bf009 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/PythonConstants.cs @@ -0,0 +1,48 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + + +using System; + +namespace Microsoft.IronPythonTools { + internal static class PythonConstants { + //Language name + public const string LanguageName = "IronPython"; + public const string TextEditorSettingsRegistryKey = LanguageName; + public const string FileExtension = ".py"; + public const string ProjectImageList = "Microsoft.PythonImageList.bmp"; + + //All IronPython guids start with 888888 + public const string LanguageServiceGuid = "88888853-c22f-4764-85d0-0ba0e096ee81"; + public const string LibraryManagerGuid = "888888e5-b976-4366-9e98-e7bc01f1842c"; + public const string LibraryManagerServiceGuid = "88888859-2f95-416e-9e2b-cac4678e5af7"; + public const string ProjectFactoryGuid = "888888a0-9f3d-457c-b088-3a5042f75d52"; + public const string EditorFactoryGuid = "888888c4-36f9-4453-90aa-29fa4d2e5706"; + public const string ProjectNodeGuid = "8888881a-afb8-42b1-8398-e60d69ee864d"; + public const string GeneralPropertyPageGuid = "888888fd-3c4a-40da-aefb-5ac10f5e8b30"; + + // Do not change below info without re-requesting PLK: + public const string ProjectSystemPackageGuid = "88888804-3158-4033-ab7d-7f4d5fc9085d"; //matches PLK + + //IRONPYTHONPATH environment variable + public const string IronPythonPath = "IRONPYTHONPATH"; + + //IDs of the icons for product registration (see Resources.resx) + public const int IconIfForSplashScreen = 300; + public const int IconIdForAboutBox = 400; + + public const string DebugStandardLibrary = "DebugStdLib"; + + } +} diff --git a/Tools/IronStudio/IronPythonTools/PythonImageList.bmp b/Tools/IronStudio/IronPythonTools/PythonImageList.bmp new file mode 100644 index 0000000000..52086d4fb7 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/PythonImageList.bmp differ diff --git a/Tools/IronStudio/IronPythonTools/PythonProject.ico b/Tools/IronStudio/IronPythonTools/PythonProject.ico new file mode 100644 index 0000000000..d682160a03 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/PythonProject.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Resources.Designer.cs b/Tools/IronStudio/IronPythonTools/Resources.Designer.cs new file mode 100644 index 0000000000..c203abe7bb --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Resources.Designer.cs @@ -0,0 +1,135 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30128.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.IronPythonTools { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Can not create tool window.. + /// + internal static string CanNotCreateWindow { + get { + return ResourceManager.GetString("CanNotCreateWindow", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to . + /// + internal static string ExecutingStartupScript { + get { + return ResourceManager.GetString("ExecutingStartupScript", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to . + /// + internal static string InitializingFromProject { + get { + return ResourceManager.GetString("InitializingFromProject", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to . + /// + internal static string MissingStartupScript { + get { + return ResourceManager.GetString("MissingStartupScript", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to References. + /// + internal static string ReferencesNodeName { + get { + return ResourceManager.GetString("ReferencesNodeName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to . + /// + internal static string SettingWorkDir { + get { + return ResourceManager.GetString("SettingWorkDir", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to IronPython Command Line. + /// + internal static string ToolWindowTitle { + get { + return ResourceManager.GetString("ToolWindowTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to . + /// + internal static string UpdatingSearchPath { + get { + return ResourceManager.GetString("UpdatingSearchPath", resourceCulture); + } + } + } +} diff --git a/Tools/IronStudio/IronPythonTools/Resources.resx b/Tools/IronStudio/IronPythonTools/Resources.resx new file mode 100644 index 0000000000..b78ebf817b --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Resources.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Can not create tool window. + + + + + + + + + + + + References + + + + + + IronPython Command Line + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/Resources/Images_24bit.bmp b/Tools/IronStudio/IronPythonTools/Resources/Images_24bit.bmp new file mode 100644 index 0000000000..157956af5a Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Resources/Images_24bit.bmp differ diff --git a/Tools/IronStudio/IronPythonTools/Resources/Images_32bit.bmp b/Tools/IronStudio/IronPythonTools/Resources/Images_32bit.bmp new file mode 100644 index 0000000000..d34a4a9012 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Resources/Images_32bit.bmp differ diff --git a/Tools/IronStudio/IronPythonTools/Resources/Images_32bit.png b/Tools/IronStudio/IronPythonTools/Resources/Images_32bit.png new file mode 100644 index 0000000000..ad73010246 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Resources/Images_32bit.png differ diff --git a/Tools/IronStudio/IronPythonTools/Resources/Package.ico b/Tools/IronStudio/IronPythonTools/Resources/Package.ico new file mode 100644 index 0000000000..ea3b23fe8d Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Resources/Package.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Resources/PythonPackageIcons.bmp b/Tools/IronStudio/IronPythonTools/Resources/PythonPackageIcons.bmp new file mode 100644 index 0000000000..1bef290d92 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Resources/PythonPackageIcons.bmp differ diff --git a/Tools/IronStudio/IronPythonTools/Resources/completionset.bmp b/Tools/IronStudio/IronPythonTools/Resources/completionset.bmp new file mode 100644 index 0000000000..d4ea8c21a2 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Resources/completionset.bmp differ diff --git a/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPackage/EmptyPackage.zip b/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPackage/EmptyPackage.zip new file mode 100644 index 0000000000..2f7eaf5425 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPackage/EmptyPackage.zip differ diff --git a/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPackage/__TemplateIcon.ico b/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPackage/__TemplateIcon.ico new file mode 100644 index 0000000000..eb0188d4d0 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPackage/__TemplateIcon.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPackage/package.vstemplate b/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPackage/package.vstemplate new file mode 100644 index 0000000000..447509ee1e --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPackage/package.vstemplate @@ -0,0 +1,13 @@ + + + Python Package + An empty Python package + __TemplateIcon.ico + IronPython + package + + + + package\__init__.py + + diff --git a/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPackage/package/__init__.py b/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPackage/package/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPyFile/EmptyPyFile.zip b/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPyFile/EmptyPyFile.zip new file mode 100644 index 0000000000..57447f22de Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPyFile/EmptyPyFile.zip differ diff --git a/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPyFile/__TemplateIcon.ico b/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPyFile/__TemplateIcon.ico new file mode 100644 index 0000000000..eb0188d4d0 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPyFile/__TemplateIcon.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPyFile/empty.py b/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPyFile/empty.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPyFile/empty.vstemplate b/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPyFile/empty.vstemplate new file mode 100644 index 0000000000..e69cab6b5d --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Files/EmptyPyFile/empty.vstemplate @@ -0,0 +1,13 @@ + + + Empty Python File + An empty .py file + __TemplateIcon.ico + IronPython + module.py + + + + empty.py + + diff --git a/Tools/IronStudio/IronPythonTools/Templates/Files/PyClass/PyClass.zip b/Tools/IronStudio/IronPythonTools/Templates/Files/PyClass/PyClass.zip new file mode 100644 index 0000000000..63dd04eb8b Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Templates/Files/PyClass/PyClass.zip differ diff --git a/Tools/IronStudio/IronPythonTools/Templates/Files/PyClass/__TemplateIcon.ico b/Tools/IronStudio/IronPythonTools/Templates/Files/PyClass/__TemplateIcon.ico new file mode 100644 index 0000000000..eb0188d4d0 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Templates/Files/PyClass/__TemplateIcon.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Templates/Files/PyClass/class.py b/Tools/IronStudio/IronPythonTools/Templates/Files/PyClass/class.py new file mode 100644 index 0000000000..cef3584d8b --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Files/PyClass/class.py @@ -0,0 +1,4 @@ +class $safeitemname$: + """description of class""" + + diff --git a/Tools/IronStudio/IronPythonTools/Templates/Files/PyClass/class.vstemplate b/Tools/IronStudio/IronPythonTools/Templates/Files/PyClass/class.vstemplate new file mode 100644 index 0000000000..111eae6d04 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Files/PyClass/class.vstemplate @@ -0,0 +1,13 @@ + + + Python class + An .py file with an empty class + __TemplateIcon.ico + IronPython + class.py + + + + class.py + + diff --git a/Tools/IronStudio/IronPythonTools/Templates/Files/WpfWindow/WpfWindow.vstemplate b/Tools/IronStudio/IronPythonTools/Templates/Files/WpfWindow/WpfWindow.vstemplate new file mode 100644 index 0000000000..8e60be2a6e --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Files/WpfWindow/WpfWindow.vstemplate @@ -0,0 +1,13 @@ + + + WPF Window + A WPF XAML file containing a window + __TemplateIcon.ico + IronPython + Window + + + + WpfWindow.xaml + + diff --git a/Tools/IronStudio/IronPythonTools/Templates/Files/WpfWindow/WpfWindow.xaml b/Tools/IronStudio/IronPythonTools/Templates/Files/WpfWindow/WpfWindow.xaml new file mode 100644 index 0000000000..0d6ecc7ee7 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Files/WpfWindow/WpfWindow.xaml @@ -0,0 +1,7 @@ + + + + + diff --git a/Tools/IronStudio/IronPythonTools/Templates/Files/WpfWindow/WpfWindow.zip b/Tools/IronStudio/IronPythonTools/Templates/Files/WpfWindow/WpfWindow.zip new file mode 100644 index 0000000000..516bd81c01 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Templates/Files/WpfWindow/WpfWindow.zip differ diff --git a/Tools/IronStudio/IronPythonTools/Templates/Files/WpfWindow/__TemplateIcon.ico b/Tools/IronStudio/IronPythonTools/Templates/Files/WpfWindow/__TemplateIcon.ico new file mode 100644 index 0000000000..eb0188d4d0 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Templates/Files/WpfWindow/__TemplateIcon.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/ConsoleAppProject/ConsoleApp.zip b/Tools/IronStudio/IronPythonTools/Templates/Projects/ConsoleAppProject/ConsoleApp.zip new file mode 100644 index 0000000000..0452d0887f Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Templates/Projects/ConsoleAppProject/ConsoleApp.zip differ diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/ConsoleAppProject/IPConsoleApp.vstemplate b/Tools/IronStudio/IronPythonTools/Templates/Projects/ConsoleAppProject/IPConsoleApp.vstemplate new file mode 100644 index 0000000000..2864c42a6c --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Projects/ConsoleAppProject/IPConsoleApp.vstemplate @@ -0,0 +1,18 @@ + + + Console Application + A project for creating a command-line application + __TemplateIcon.ico + IronPython + 50 + 2 + true + ConsoleApplication + true + + + + Program.py + + + diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/ConsoleAppProject/IronPythonApp.pyproj b/Tools/IronStudio/IronPythonTools/Templates/Projects/ConsoleAppProject/IronPythonApp.pyproj new file mode 100644 index 0000000000..7e61f25143 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Projects/ConsoleAppProject/IronPythonApp.pyproj @@ -0,0 +1,22 @@ + + + Debug + 2.0 + $guid1$ + . + Program.py + + . + + + true + false + + + true + false + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/ConsoleAppProject/Program.py b/Tools/IronStudio/IronPythonTools/Templates/Projects/ConsoleAppProject/Program.py new file mode 100644 index 0000000000..285c308784 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Projects/ConsoleAppProject/Program.py @@ -0,0 +1 @@ +print 'Hello World' diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/ConsoleAppProject/__TemplateIcon.ico b/Tools/IronStudio/IronPythonTools/Templates/Projects/ConsoleAppProject/__TemplateIcon.ico new file mode 100644 index 0000000000..8cc00e6854 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Templates/Projects/ConsoleAppProject/__TemplateIcon.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/Program.html b/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/Program.html new file mode 100644 index 0000000000..82a117cdba --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/Program.html @@ -0,0 +1,15 @@ + + + + + + + $projectname$ + + + + + + diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/Program.py b/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/Program.py new file mode 100644 index 0000000000..f66347ca6a --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/Program.py @@ -0,0 +1,4 @@ +def SayHello(s,e): + window.Alert("Hello, World!") + +document.Button1.events.onclick += SayHello diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/SilverlightProject.pyproj b/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/SilverlightProject.pyproj new file mode 100644 index 0000000000..20482e6086 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/SilverlightProject.pyproj @@ -0,0 +1,23 @@ + + + Debug + 2.0 + $guid1$ + . + $safeprojectname$.html + + . + + + true + false + + + true + false + + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/SilverlightProject.vstemplate b/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/SilverlightProject.vstemplate new file mode 100644 index 0000000000..43a8e8fb9b --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/SilverlightProject.vstemplate @@ -0,0 +1,17 @@ + + + IronPython Silverlight Web Page + A project for creating a web page scripted on the client side with IronPython via Silverlight + __TemplateIcon.ico + IronPython + true + SilverlightPage + true + + + + Program.html + Program.py + + + diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/SilverlightProject.zip b/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/SilverlightProject.zip new file mode 100644 index 0000000000..b84966e10a Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/SilverlightProject.zip differ diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/__TemplateIcon.ico b/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/__TemplateIcon.ico new file mode 100644 index 0000000000..8cc00e6854 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Templates/Projects/SilverlightProject/__TemplateIcon.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/WinformsProject/Program.py b/Tools/IronStudio/IronPythonTools/Templates/Projects/WinformsProject/Program.py new file mode 100644 index 0000000000..8c8c7f7f14 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Projects/WinformsProject/Program.py @@ -0,0 +1,14 @@ +import clr +clr.AddReference('System.Windows.Forms') + +from System.Windows.Forms import * + +class MyForm(Form): + def __init__(self): + button = Button() + button.Text = 'Click Me' + self.Controls.Add(button) + + +form = MyForm() +Application.Run(form) diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/WinformsProject/WinformsApp.pyproj b/Tools/IronStudio/IronPythonTools/Templates/Projects/WinformsProject/WinformsApp.pyproj new file mode 100644 index 0000000000..f63f452429 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Projects/WinformsProject/WinformsApp.pyproj @@ -0,0 +1,23 @@ + + + Debug + 2.0 + $guid1$ + . + Program.py + + . + True + + + true + false + + + true + false + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/WinformsProject/WinformsApp.vstemplate b/Tools/IronStudio/IronPythonTools/Templates/Projects/WinformsProject/WinformsApp.vstemplate new file mode 100644 index 0000000000..75a584a21a --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Projects/WinformsProject/WinformsApp.vstemplate @@ -0,0 +1,18 @@ + + + WinForms Application + A project for creating a WinForms based application + __TemplateIcon.ico + IronPython + 50 + 1 + true + WindowsApplication + true + + + + Program.py + + + diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/WinformsProject/WinformsApp.zip b/Tools/IronStudio/IronPythonTools/Templates/Projects/WinformsProject/WinformsApp.zip new file mode 100644 index 0000000000..fadb7ee15e Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Templates/Projects/WinformsProject/WinformsApp.zip differ diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/WinformsProject/__TemplateIcon.ico b/Tools/IronStudio/IronPythonTools/Templates/Projects/WinformsProject/__TemplateIcon.ico new file mode 100644 index 0000000000..8cc00e6854 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Templates/Projects/WinformsProject/__TemplateIcon.ico differ diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/Program.py b/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/Program.py new file mode 100644 index 0000000000..86b5493580 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/Program.py @@ -0,0 +1,11 @@ +import wpf + +from System.Windows import Application, Window + +class MyWindow(Window): + def __init__(self): + wpf.LoadComponent(self, '$safeprojectname$.xaml') + + +if __name__ == '__main__': + Application().Run(MyWindow()) diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/Program.xaml b/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/Program.xaml new file mode 100644 index 0000000000..c80abc169a --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/Program.xaml @@ -0,0 +1,6 @@ + + + diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/WpfApp.pyproj b/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/WpfApp.pyproj new file mode 100644 index 0000000000..b87e27ef09 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/WpfApp.pyproj @@ -0,0 +1,24 @@ + + + Debug + 2.0 + $guid1$ + . + $safeprojectname$.py + + . + True + + + true + false + + + true + false + + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/WpfApp.vstemplate b/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/WpfApp.vstemplate new file mode 100644 index 0000000000..d2c30c4abf --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/WpfApp.vstemplate @@ -0,0 +1,22 @@ + + + Wpf Application + Windows Presentation Foundation Application + IronPython + + + 1000 + true + WpfApplication + true + Enabled + true + __TemplateIcon.ico + + + + Program.py + Program.xaml + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/WpfApp.zip b/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/WpfApp.zip new file mode 100644 index 0000000000..e7df702104 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/WpfApp.zip differ diff --git a/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/__TemplateIcon.ico b/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/__TemplateIcon.ico new file mode 100644 index 0000000000..8cc00e6854 Binary files /dev/null and b/Tools/IronStudio/IronPythonTools/Templates/Projects/WpfProject/__TemplateIcon.ico differ diff --git a/Tools/IronStudio/IronPythonTools/VSPackage.resx b/Tools/IronStudio/IronPythonTools/VSPackage.resx new file mode 100644 index 0000000000..1c8499442a --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/VSPackage.resx @@ -0,0 +1,151 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + IronPython Tools for Visual Studio + + + IronPython Tools for Visual Studio provides intellisense, project support, project and item templates, as well as a REPL window for IronPython development. + + + + Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\Images_24bit.bmp;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Text Editor! + + + IronPython + + + Console Application + + + WinForms Application + + + Wpf Application + + + IronPython Silverlight Page + + \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonTools/source.extension.vsixmanifest b/Tools/IronStudio/IronPythonTools/source.extension.vsixmanifest new file mode 100644 index 0000000000..9222f15e48 --- /dev/null +++ b/Tools/IronStudio/IronPythonTools/source.extension.vsixmanifest @@ -0,0 +1,43 @@ + + + + IronPython Tools for Visual Studio + Microsoft + 0.3 + IronPython Tools for Visual Studio provides intellisense, project support, project and item templates, as well as a REPL window for IronPython development. + 1033 + http://www.ironpython.net + IronPython Tools for VS License.rtf + http://ironpython.codeplex.com + false + + + Ultimate + Premium + Pro + IntegratedShell + VSTS + VSTD + + + + + + + IronStudio + + + + |%CurrentProject%| + IronPython + |%CurrentProject%| + |IronPythonToolsCore| + Templates\Projects\ConsoleAppProject + Templates\Projects\WinformsProject + Templates\Projects\WpfProject + Templates\Projects\SilverlightProject + Templates\Files\EmptyPyFile + Templates\Files\PyClass + Templates\Files\WpfWindow + + diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore.csproj b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore.csproj new file mode 100644 index 0000000000..db8b0d1508 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore.csproj @@ -0,0 +1,218 @@ + + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {B2E267A2-4836-40D7-817E-49D9D0E88435} + Library + Properties + Microsoft + IronPythonTools.Core + v4.0 + 512 + SAK + SAK + SAK + SAK + Client + + + true + full + false + $(SolutionDir)..\bin\Debug + DEBUG;TRACE;$(SignedSym) + prompt + 4 + true + + + pdbonly + true + $(SolutionDir)..\bin\Release + TRACE;$(SignedSym) + prompt + 4 + + + + False + $(DevEnvDir)\PublicAssemblies\Microsoft.VisualStudio.ComponentModelHost.dll + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.CoreUtility.dll + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Language.Intellisense.dll + + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Text.Data.dll + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Text.Logic.dll + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Text.UI.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {155CE436-1669-4A48-8095-410F2430237F} + IronPython.Modules + + + {95289EA9-5778-489D-AB48-F81F2CE2DA32} + IronPython %28Languages\IronPython\IronPython%29 + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Dynamic + + + {02FF0909-F5AD-48CF-A86A-345E721B7E40} + Microsoft.Scripting + + + {843716AE-38B3-4723-963C-950DD06BC4B8} + IronStudioCore + + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore.csproj.vspscc b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore.csproj.vspscc new file mode 100644 index 0000000000..feffdecaa4 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Editor/BraceMatcher.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Editor/BraceMatcher.cs new file mode 100644 index 0000000000..0a7e8a7ac1 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Editor/BraceMatcher.cs @@ -0,0 +1,200 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.IronStudio.Core; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Language.StandardClassification; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Adornments; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; + +namespace Microsoft.IronPythonTools.Editor.Core { + /// + /// Provides highlighting of matching braces in a text view. + /// + public class BraceMatcher { + private readonly ITextView _textView; + private SimpleTagger _textMarkerTagger; + private readonly IComponentModel _compModel; + private bool _hasSpan; + private static TextMarkerTag _tag = new TextMarkerTag("bracehighlight"); + + /// + /// Starts watching the provided text view for brace matching. When new braces are inserted + /// in the text or when the cursor moves to a brace the matching braces are highlighted. + /// + public static void WatchBraceHighlights(ITextView view, IComponentModel componentModel) { + var matcher = new BraceMatcher(view, componentModel); + + // position changed only fires when the caret is explicitly moved, not from normal text edits, + // so we track both changes and position changed. + view.Caret.PositionChanged += matcher.CaretPositionChanged; + view.TextBuffer.Changed += matcher.TextBufferChanged; + } + + public BraceMatcher(ITextView view, IComponentModel componentModel) { + _textView = view; + _compModel = componentModel; + } + + private void CaretPositionChanged(object sender, CaretPositionChangedEventArgs e) { + RemoveExistingHighlights(); + + UpdateBraceMatching(e.NewPosition.BufferPosition.Position); + } + + private void TextBufferChanged(object sender, TextContentChangedEventArgs changed) { + RemoveExistingHighlights(); + + if (changed.Changes.Count == 1) { + var newText = changed.Changes[0].NewText; + if (newText == ")" || newText == "}" || newText == "]") { + UpdateBraceMatching(changed.Changes[0].NewPosition + 1); + } + } + } + + private void UpdateBraceMatching(int pos) { + if (pos != 0) { + var prevCharText = _textView.TextBuffer.CurrentSnapshot.GetText(pos - 1, 1); + if (prevCharText == ")" || prevCharText == "]" || prevCharText == "}") { + if (HighlightBrace(GetBraceKind(prevCharText), pos, -1)) { + return; + } + } + } + + if (pos != _textView.TextBuffer.CurrentSnapshot.Length) { + var nextCharText = _textView.TextBuffer.CurrentSnapshot.GetText(pos, 1); + if (nextCharText == "(" || nextCharText == "[" || nextCharText == "{") { + HighlightBrace(GetBraceKind(nextCharText), pos + 1, 1); + } + } + } + + private void RemoveExistingHighlights() { + if (_hasSpan && _textMarkerTagger != null) { + TextMarker.RemoveTagSpans(x => true); + _hasSpan = false; + } + } + + private SimpleTagger TextMarker { + get { + if (_textMarkerTagger == null) { + _textMarkerTagger = _compModel.GetService().GetTextMarkerTagger(_textView.TextBuffer); + } + return _textMarkerTagger; + } + } + + private bool HighlightBrace(BraceKind brace, int position, int direction) { + var classifier = _textView.TextBuffer.GetDlrClassifier(); + var snapshot = _textView.TextBuffer.CurrentSnapshot; + var span = new SnapshotSpan(snapshot, position - 1, 1); + var originalSpan = span; + + var spans = classifier.GetClassificationSpans(span); + // we don't highlight braces if we're in a comment or string literal + if (spans.Count == 0 || + (spans.Count == 1 && + (!spans[0].ClassificationType.IsOfType(PredefinedClassificationTypeNames.String) && + !spans[0].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Comment)))) { + + // find the opening span + var curLine = snapshot.GetLineFromPosition(position); + int curLineNo = curLine.LineNumber; + + if (direction == 1) { + span = new SnapshotSpan(snapshot, position, curLine.End.Position - position); + } else { + span = new SnapshotSpan(curLine.Start, position - curLine.Start.Position + direction); + } + + int depth = 1; + for (; ; ) { + spans = classifier.GetClassificationSpans(span); + for (int i = direction == -1 ? spans.Count - 1 : 0; i >= 0 && i < spans.Count; i += direction) { + if (IsCloseSpan(spans, i)) { + if (IsSameBraceKind(spans[i].Span.GetText(), brace)) { + depth -= direction; + } + } else if (IsOpenSpan(spans, i)) { + if (IsSameBraceKind(spans[i].Span.GetText(), brace)) { + depth += direction; + } + } + + if (depth == 0) { + _hasSpan = true; + // left brace + TextMarker.CreateTagSpan(snapshot.CreateTrackingSpan(spans[i].Span, SpanTrackingMode.EdgeExclusive), _tag); + // right brace + TextMarker.CreateTagSpan(snapshot.CreateTrackingSpan(new SnapshotSpan(snapshot, position - 1, 1), SpanTrackingMode.EdgeExclusive), _tag); + return true; + } + } + + curLineNo += direction; + if (curLineNo < 0 || curLineNo >= snapshot.LineCount) { + break; + } + + var line = snapshot.GetLineFromLineNumber(curLineNo); + span = new SnapshotSpan(line.Start, line.End); + } + } + return false; + } + + private enum BraceKind { + Bracket, + Paren, + Brace + } + + private static bool IsSameBraceKind(string brace, BraceKind kind) { + return GetBraceKind(brace) == kind; + } + + private static BraceKind GetBraceKind(string brace) { + switch (brace[0]) { + case '[': + case ']': return BraceKind.Bracket; + case '(': + case ')': return BraceKind.Paren; + case '{': + case '}': return BraceKind.Bracket; + default: throw new InvalidOperationException(); + } + } + + private static bool IsOpenSpan(IList spans, int i) { + return spans[i].ClassificationType == PythonClassifierProvider.Instance.OpenGroupingClassification || + (spans[i].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Operator) && + spans[i].Span.Length == 1 && + (spans[i].Span.GetText() == "{" || spans[i].Span.GetText() == "[")); + } + + private static bool IsCloseSpan(IList spans, int i) { + return spans[i].ClassificationType == PythonClassifierProvider.Instance.CloseGroupingClassification || + (spans[i].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Operator) && + spans[i].Span.Length == 1 && + (spans[i].Span.GetText() == "}" || spans[i].Span.GetText() == "]")); + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Editor/EditorExtensions.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Editor/EditorExtensions.cs new file mode 100644 index 0000000000..260fbaa422 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Editor/EditorExtensions.cs @@ -0,0 +1,131 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.IronPythonTools.Editor.Core { + public static class EditorExtensions { + /// + /// Adds comment characters (#) to the start of each line. If there is a selection the comment is applied + /// to each selected line. Otherwise the comment is applied to the current line. + /// + /// + public static void CommentBlock(this ITextView view) { + if (view.Selection.IsActive) { + // comment every line in the selection + CommentRegion(view, view.Selection.Start.Position, view.Selection.End.Position); + } else { + // comment the current line + CommentRegion(view, view.Caret.Position.BufferPosition, view.Caret.Position.BufferPosition); + } + } + + /// + /// Removes a comment character (#) from the start of each line. If there is a selection the character is + /// removed from each selected line. Otherwise the character is removed from the current line. Uncommented + /// lines are ignored. + /// + /// + public static void UncommentBlock(this ITextView view) { + if (view.Selection.IsActive) { + // uncomment every line in the selection + UncommentRegion(view, view.Selection.Start.Position, view.Selection.End.Position); + } else { + // uncomment the current line + UncommentRegion(view, view.Caret.Position.BufferPosition, view.Caret.Position.BufferPosition); + } + + } + + private static void CommentRegion(ITextView view, SnapshotPoint start, SnapshotPoint end) { + using (var edit = view.TextBuffer.CreateEdit()) { + int minColumn = Int32.MaxValue; + // first pass, determine the position to place the comment + for (int i = start.GetContainingLine().LineNumber; i <= end.GetContainingLine().LineNumber; i++) { + var curLine = view.TextBuffer.CurrentSnapshot.GetLineFromLineNumber(i); + var text = curLine.GetText(); + + minColumn = Math.Min(GetMinimumColumn(text), minColumn); + } + + // second pass, place the comment + for (int i = start.GetContainingLine().LineNumber; i <= end.GetContainingLine().LineNumber; i++) { + var curLine = view.TextBuffer.CurrentSnapshot.GetLineFromLineNumber(i); + if (curLine.Length < minColumn) { + // need extra white space + edit.Insert(curLine.Start.Position + curLine.Length, new String(' ', minColumn - curLine.Length) + "#"); + } else { + edit.Insert(curLine.Start.Position + minColumn, "#"); + } + } + + edit.Apply(); + } + + // select the full region we just commented + UpdateSelection(view, start, end); + } + + private static int GetMinimumColumn(string text) { + for (int j = 0; j < text.Length; j++) { + if (!Char.IsWhiteSpace(text[j])) { + return j; + } + } + return Int32.MaxValue; + } + + private static void UncommentRegion(ITextView view, SnapshotPoint start, SnapshotPoint end) { + using (var edit = view.TextBuffer.CreateEdit()) { + + // first pass, determine the position to place the comment + for (int i = start.GetContainingLine().LineNumber; i <= end.GetContainingLine().LineNumber; i++) { + var curLine = view.TextBuffer.CurrentSnapshot.GetLineFromLineNumber(i); + + DeleteFirstCommentChar(edit, curLine); + } + + edit.Apply(); + } + + // select the full region we just uncommented + + UpdateSelection(view, start, end); + } + + private static void UpdateSelection(ITextView view, SnapshotPoint start, SnapshotPoint end) { + view.Selection.Select( + new SnapshotSpan( + start.GetContainingLine().Start.TranslateTo(view.TextBuffer.CurrentSnapshot, PointTrackingMode.Negative), + end.GetContainingLine().End.TranslateTo(view.TextBuffer.CurrentSnapshot, PointTrackingMode.Positive) + ), + false + ); + } + + private static void DeleteFirstCommentChar(ITextEdit edit, ITextSnapshotLine curLine) { + var text = curLine.GetText(); + for (int j = 0; j < text.Length; j++) { + if (!Char.IsWhiteSpace(text[j])) { + if (text[j] == '#') { + edit.Delete(curLine.Start.Position + j, 1); + } + break; + } + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Extensions.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Extensions.cs new file mode 100644 index 0000000000..374e7d4bae --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Extensions.cs @@ -0,0 +1,110 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; + +using Microsoft.IronPythonTools.Intellisense; +using Microsoft.PyAnalysis; +using Microsoft.Scripting.Hosting; + +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.IronPythonTools.Internal { + public static class Extensions { + public static StandardGlyphGroup ToGlyphGroup(this ResultType objectType) { + StandardGlyphGroup group; + switch (objectType) { + case ResultType.Class: group = StandardGlyphGroup.GlyphGroupClass; break; + case ResultType.DelegateInstance: + case ResultType.Delegate: group = StandardGlyphGroup.GlyphGroupDelegate; break; + case ResultType.Enum: group = StandardGlyphGroup.GlyphGroupEnum; break; + case ResultType.Namespace: group = StandardGlyphGroup.GlyphGroupNamespace; break; + case ResultType.Multiple: group = StandardGlyphGroup.GlyphGroupOverload; break; + case ResultType.Field: group = StandardGlyphGroup.GlyphGroupField; break; + case ResultType.Module: group = StandardGlyphGroup.GlyphGroupModule; break; + case ResultType.Property: group = StandardGlyphGroup.GlyphGroupProperty; break; + case ResultType.Instance: group = StandardGlyphGroup.GlyphGroupVariable; break; + case ResultType.Constant: group = StandardGlyphGroup.GlyphGroupConstant; break; + case ResultType.EnumInstance: group = StandardGlyphGroup.GlyphGroupEnumMember; break; + case ResultType.Event: group = StandardGlyphGroup.GlyphGroupEvent; break; + case ResultType.Function: + case ResultType.Method: + default: + group = StandardGlyphGroup.GlyphGroupMethod; + break; + } + return group; + } + + internal static bool TryGetAnalysis(this ITextBuffer buffer, out IProjectEntry analysis) { + return buffer.Properties.TryGetProperty(typeof(IProjectEntry), out analysis); + } + + internal static bool TryGetPythonAnalysis(this ITextBuffer buffer, out IPythonProjectEntry analysis) { + IProjectEntry entry; + if (buffer.TryGetAnalysis(out entry) && (analysis = entry as IPythonProjectEntry) != null) { + return true; + } + analysis = null; + return false; + } + + internal static IProjectEntry GetAnalysis(this ITextBuffer buffer) { + IProjectEntry res; + buffer.TryGetAnalysis(out res); + return res; + } + + internal static IPythonProjectEntry GetPythonAnalysis(this ITextBuffer buffer) { + IPythonProjectEntry res; + buffer.TryGetPythonAnalysis(out res); + return res; + } + + internal static string GetFilePath(this ITextView textView) { + return textView.TextBuffer.GetFilePath(); + } + + internal static string GetFilePath(this ITextBuffer textBuffer) { + ITextDocument textDocument; + if (textBuffer.Properties.TryGetProperty(typeof(ITextDocument), out textDocument)) { + return textDocument.FilePath; + } else { + return null; + } + } + + internal static ITrackingSpan CreateTrackingSpan(this IIntellisenseSession session, ITextBuffer buffer) { + var triggerPoint = session.GetTriggerPoint(buffer); + var position = session.GetTriggerPoint(buffer).GetPosition(session.TextView.TextSnapshot); + + var snapshot = buffer.CurrentSnapshot; + if (position == snapshot.Length) { + return snapshot.CreateTrackingSpan(position, 0, SpanTrackingMode.EdgeInclusive); + } else { + return snapshot.CreateTrackingSpan(position, 1, SpanTrackingMode.EdgeInclusive); + } + } + + internal static ITrackingSpan CreateTrackingSpan0(this IIntellisenseSession session, ITextBuffer buffer) { + var triggerPoint = session.GetTriggerPoint(buffer); + var position = session.GetTriggerPoint(buffer).GetPosition(session.TextView.TextSnapshot); + + var snapshot = buffer.CurrentSnapshot; + return snapshot.CreateTrackingSpan(position, 0, SpanTrackingMode.EdgeInclusive); + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/IPythonRuntimeHost.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/IPythonRuntimeHost.cs new file mode 100644 index 0000000000..d0efb31968 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/IPythonRuntimeHost.cs @@ -0,0 +1,48 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Hosting; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronPythonTools { + public interface IPythonRuntimeHost { + ScriptEngine ScriptEngine { + get; + } + + /// + /// The content type for the IronPython content. + /// + IContentType ContentType { + get; + } + + bool EnterOutliningModeOnOpen { + get; + set; + } + + bool IntersectMembers { + get; + set; + } + + bool HideAdvancedMembers { + get; + set; + } + + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/CompletionAnalysis.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/CompletionAnalysis.cs new file mode 100644 index 0000000000..f5b5430543 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/CompletionAnalysis.cs @@ -0,0 +1,302 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.IronPythonTools.Internal; +using Microsoft.IronStudio; +using Microsoft.PyAnalysis; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using System.Diagnostics; + +namespace Microsoft.IronPythonTools.Intellisense { + /// + /// Provides various completion services after the text around the current location has been + /// processed. The completion services are specific to the current context + /// + public class CompletionAnalysis { + private readonly string _text; + protected readonly int _pos; + private readonly ITrackingSpan _span; + private readonly ITextBuffer _textBuffer; + internal const Int64 TooMuchTime = 50; + protected static Stopwatch _stopwatch = MakeStopWatch(); + + internal static CompletionAnalysis EmptyCompletionContext = new CompletionAnalysis(String.Empty, 0, null, null); + + internal CompletionAnalysis(string text, int pos, ITrackingSpan span, ITextBuffer textBuffer) { + _text = text ?? String.Empty; + _pos = pos; + _span = span; + _textBuffer = textBuffer; + } + +#if FALSE + /// + /// Gets a CompletionContext providing a list of possible members the user can dot through. + /// + public static CompletionContext GetMemberCompletionContext(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span) { + ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, buffer, span); + + var loc = parser.Span.GetSpan(parser.Snapshot.Version); + var line = parser.Snapshot.GetLineFromPosition(loc.Start); + var lineStart = line.Start; + + var textLen = loc.End - lineStart.Position; + if (textLen <= 0) { + // Ctrl-Space on an empty line, we just want to get global vars + return new NormalCompletionContext(String.Empty, loc.Start, parser.Snapshot, parser.Span, parser.Buffer, 0); + } + + return TrySpecialCompletions(parser, loc) ?? + GetNormalCompletionContext(parser, loc); + } + + /// + /// Gets a CompletionContext for the expression at the provided span. If the span is in + /// part of an identifier then the expression is extended to complete the identifier. + /// + public static CompletionContext GetExpressionContext(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span) { + ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, buffer, span); + + var loc = parser.Span.GetSpan(parser.Snapshot.Version); + var exprRange = parser.GetExpressionRange(); + if (exprRange == null) { + return EmptyCompletionContext; + } + + // extend right for any partial expression the user is hovering on, for example: + // "x.Baz" where the user is hovering over the B in baz we want the complete + // expression. + var text = exprRange.Value.GetText(); + var endingLine = exprRange.Value.End.GetContainingLine(); + var endText = snapshot.GetText(exprRange.Value.End.Position, endingLine.End.Position - exprRange.Value.End.Position); + for (int i = 0; i < endText.Length; i++) { + if (!Char.IsLetterOrDigit(endText[i]) && endText[i] != '_') { + text += endText.Substring(0, i); + break; + } + } + + var applicableSpan = parser.Snapshot.CreateTrackingSpan( + exprRange.Value.Span, + SpanTrackingMode.EdgeExclusive + ); + + return new NormalCompletionContext( + text, + loc.Start, + parser.Snapshot, + applicableSpan, + parser.Buffer, + -1 + ); + } + + public static CompletionContext GetSignatureContext(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span) { + ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, buffer, span); + + var loc = parser.Span.GetSpan(parser.Snapshot.Version); + + int paramIndex; + var exprRange = parser.GetExpressionRange(1, out paramIndex); + if (exprRange == null) { + return EmptyCompletionContext; + } + var text = exprRange.Value.GetText(); + + var applicableSpan = parser.Snapshot.CreateTrackingSpan( + exprRange.Value.Span, + SpanTrackingMode.EdgeExclusive + ); + + return new NormalCompletionContext( + text, + loc.Start, + parser.Snapshot, + applicableSpan, + parser.Buffer, + paramIndex + ); + } + +#endif + public ITextBuffer TextBuffer { + get { + return _textBuffer; + } + } + + public string Text { + get { + return _text; + } + } + + public ITrackingSpan Span { + get { + return _span; + } + } +#if FALSE + public virtual int ParameterIndex { + get { return 0; } + } +#endif + public virtual CompletionSet GetCompletions(IGlyphService glyphService) { + return null; + } + +#if FALSE + public virtual ISignature[] GetSignatures() { + return new ISignature[0]; + } + + public virtual string GetQuickInfo() { + return null; + } + + public virtual IEnumerable GetVariableInfo() { + yield break; + } + + private static CompletionContext GetNormalCompletionContext(ReverseExpressionParser parser, Span loc) { + var exprRange = parser.GetExpressionRange(); + if (exprRange == null) { + return EmptyCompletionContext; + } + var text = exprRange.Value.GetText(); + + var applicableSpan = parser.Snapshot.CreateTrackingSpan( + exprRange.Value.Span, + SpanTrackingMode.EdgeExclusive + ); + + return new NormalCompletionContext( + text, + loc.Start, + parser.Snapshot, + applicableSpan, + parser.Buffer, + -1 + ); + } + + private static CompletionContext TrySpecialCompletions(ReverseExpressionParser parser, Span loc) { + if (parser.Tokens.Count > 0) { + // Check for context-sensitive intellisense + var lastClass = parser.Tokens[parser.Tokens.Count - 1]; + + if (lastClass.ClassificationType == parser.Classifier.Provider.Comment) { + // No completions in comments + return EmptyCompletionContext; + } else if (lastClass.ClassificationType == parser.Classifier.Provider.StringLiteral) { + // String completion + return new StringLiteralCompletionContext(lastClass.Span.GetText(), loc.Start, parser.Span, parser.Buffer); + } + + // Import completions + var first = parser.Tokens[0]; + if (IsKeyword(first, "import")) { + return ImportCompletionContext.Make(first, lastClass, loc, parser.Snapshot, parser.Span, parser.Buffer, IsSpaceCompletion(parser, loc)); + } else if (IsKeyword(first, "from")) { + return FromImportCompletionContext.Make(parser.Tokens, first, loc, parser.Snapshot, parser.Span, parser.Buffer, IsSpaceCompletion(parser, loc)); + } + return null; + } + + return EmptyCompletionContext; + } + + private static bool IsSpaceCompletion(ReverseExpressionParser parser, Span loc) { + var keySpan = new SnapshotSpan(parser.Snapshot, loc.Start - 1, 1); + return (keySpan.GetText() == " "); + } +#endif + internal static bool IsKeyword(ClassificationSpan token, string keyword) { + return token.ClassificationType.Classification == "keyword" && token.Span.GetText() == keyword; + } + + internal static Completion PythonCompletion(IGlyphService service, MemberResult memberResult) { + StandardGlyphGroup group = memberResult.MemberType.ToGlyphGroup(); + var icon = new IconDescription(group, StandardGlyphItem.GlyphItemPublic); + + var result = new LazyCompletion(memberResult.Name, () => memberResult.Completion, () => memberResult.ToolTip, service.GetGlyph(group, StandardGlyphItem.GlyphItemPublic)); + result.Properties.AddProperty(typeof(IconDescription), icon); + return result; + } + + internal static Completion PythonCompletion(IGlyphService service, string name, string tooltip, StandardGlyphGroup group) { + var icon = new IconDescription(group, StandardGlyphItem.GlyphItemPublic); + + var result = new LazyCompletion(name, () => name, () => tooltip, service.GetGlyph(group, StandardGlyphItem.GlyphItemPublic)); + result.Properties.AddProperty(typeof(IconDescription), icon); + return result; + } + + internal ModuleAnalysis GetAnalysisEntry() { + return ((IPythonProjectEntry)TextBuffer.GetAnalysis()).Analysis; + } + + private static Stopwatch MakeStopWatch() { + var res = new Stopwatch(); + res.Start(); + return res; + } + + public virtual Completion[] GetModules(IGlyphService glyphService, string text) { + var analysis = GetAnalysisEntry(); + var path = text.Split('.'); + if (path.Length > 0) { + // path = path[:-1] + var newPath = new string[path.Length - 1]; + Array.Copy(path, newPath, path.Length - 1); + path = newPath; + } + + MemberResult[] modules = new MemberResult[0]; + if (path.Length == 0) { + if (analysis != null) { + modules = analysis.ProjectState.GetModules(); + } + +#if REPL + var repl = Intellisense.GetRepl(_textBuffer); + if (repl != null) { + modules = Intellisense.MergeMembers(modules, repl.GetModules()); + } +#endif + } else { + if (analysis != null) { + modules = analysis.ProjectState.GetModuleMembers(path); + } + } + + var sortedAndFiltered = NormalCompletionAnalysis.FilterCompletions(modules, text, (x, y) => x.StartsWith(y)); + Array.Sort(sortedAndFiltered, NormalCompletionAnalysis.ModuleSort); + + var result = new Completion[sortedAndFiltered.Length]; + for (int i = 0; i < sortedAndFiltered.Length; i++) { + result[i] = PythonCompletion(glyphService, sortedAndFiltered[i]); + } + return result; + } + + public override string ToString() { + return String.Format("CompletionContext({0}): {1} @{2}", GetType().Name, Text, _pos); + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/CompletionSource.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/CompletionSource.cs new file mode 100644 index 0000000000..7c65edb496 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/CompletionSource.cs @@ -0,0 +1,51 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.IronPythonTools.Internal; +using Microsoft.IronStudio.Core; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronPythonTools.Intellisense { + class CompletionSource : ICompletionSource { + private readonly ITextBuffer _textBuffer; + private readonly CompletionSourceProvider _provider; + private readonly IPythonRuntimeHost _host; + + public CompletionSource(CompletionSourceProvider provider, ITextBuffer textBuffer, IPythonRuntimeHost host) { + _textBuffer = textBuffer; + _provider = provider; + _host = host; + } + + public void AugmentCompletionSession(ICompletionSession session, IList completionSets) { + var textView = session.TextView; + var textBuffer = session.TextView.TextBuffer; + var span = session.CreateTrackingSpan0(textBuffer); + var provider = _provider._Analysis.GetCompletions(textBuffer.CurrentSnapshot, textBuffer, span, _host.IntersectMembers, _host.HideAdvancedMembers); + + var completions = provider.GetCompletions(_provider._glyphService); + + if (completions == null || completions.Completions.Count == 0) { + return; + } + + completionSets.Add(completions); + } + + public void Dispose() { + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/CompletionSourceProvider.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/CompletionSourceProvider.cs new file mode 100644 index 0000000000..f7d8f99426 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/CompletionSourceProvider.cs @@ -0,0 +1,42 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel.Composition; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Core; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Utilities; +using Microsoft.IronPythonTools.Library; + +namespace Microsoft.IronPythonTools.Intellisense { + [Export(typeof(ICompletionSourceProvider)), ContentType(PythonCoreConstants.ContentType), Order, Name("CompletionProvider")] + internal class CompletionSourceProvider : ICompletionSourceProvider { + private readonly IPythonRuntimeHost _host; + + [Import] + internal IGlyphService _glyphService = null; // Assigned from MEF + [Import] + internal IPythonAnalyzer _Analysis; + + [ImportingConstructor] + public CompletionSourceProvider(IPythonRuntimeHost host) { + _host = host; + } + + public ICompletionSource TryCreateCompletionSource(ITextBuffer textBuffer) { + return new CompletionSource(this, textBuffer, _host); + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/ExpressionAnalysis.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/ExpressionAnalysis.cs new file mode 100644 index 0000000000..1d247d14aa --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/ExpressionAnalysis.cs @@ -0,0 +1,73 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.PyAnalysis; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronPythonTools.Intellisense { + /// + /// Provides the results of analyzing a simple expression. Returned from Analysis.AnalyzeExpression. + /// + public class ExpressionAnalysis { + private readonly string _expr; + private readonly ModuleAnalysis _analysis; + private readonly ITrackingSpan _span; + private readonly int _lineNo; + public static readonly ExpressionAnalysis Empty = new ExpressionAnalysis("", null, 0, null); + + internal ExpressionAnalysis(string expression, ModuleAnalysis analysis, int lineNo, ITrackingSpan span) { + _expr = expression; + _analysis = analysis; + _lineNo = lineNo; + _span = span; + } + + /// + /// The expression which this is providing information about. + /// + public string Expression { + get { + return _expr; + } + } + + /// + /// The span of the expression being analyzed. + /// + public ITrackingSpan Span { + get { + return _span; + } + } + + /// + /// Gets all of the variables (storage locations) associated with the expression. + /// + public IEnumerable Variables { + get { + return _analysis.GetVariables(_expr, _lineNo); + } + } + + /// + /// The possible values of the expression (types, constants, functions, modules, etc...) + /// + public IEnumerable Values { + get { + return _analysis.GetValues(_expr, _lineNo); + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/FileCookie.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/FileCookie.cs new file mode 100644 index 0000000000..e562bd39fe --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/FileCookie.cs @@ -0,0 +1,59 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.PyAnalysis; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text; +using System.IO; + +namespace Microsoft.IronPythonTools.Intellisense { + class FileCookie : IAnalysisCookie { + private readonly string _path; + private string[] _allLines; + + public FileCookie(string path) { + _path = path; + } + + public string Path { + get { + return _path; + } + } + + #region IFileCookie Members + + public string GetLine(int lineNo) { + if (_allLines == null) { + try { + _allLines = File.ReadAllLines(Path); + } catch (IOException) { + _allLines = new string[0]; + } + } + + if (lineNo - 1 < _allLines.Length) { + return _allLines[lineNo - 1]; + } + + return String.Empty; + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/FromImportCompletionAnalysis.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/FromImportCompletionAnalysis.cs new file mode 100644 index 0000000000..260d227ba5 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/FromImportCompletionAnalysis.cs @@ -0,0 +1,123 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; + +namespace Microsoft.IronPythonTools.Intellisense { + /// + /// Provides the completion context for when the user is doing an "import from" + /// + internal class FromImportCompletionAnalysis : CompletionAnalysis { + private readonly string _namespace; + + public FromImportCompletionAnalysis(string text, int pos, ITrackingSpan span, ITextBuffer textBuffer, string ns) + : base(text, pos, span, textBuffer) { + _namespace = ns; + } + + public static CompletionAnalysis Make(IList classifications, ClassificationSpan start, + Span loc, ITextSnapshot snapshot, ITrackingSpan span, ITextBuffer buffer, bool isSpace) { + if (classifications.Count == 1) { + return new ImportCompletionAnalysis(String.Empty, loc.Start, span, buffer); + } + + ClassificationSpan imp = null; + for (int i = 1; i < classifications.Count; i++) { + if (IsKeyword(classifications[i], "import")) { + imp = classifications[i]; + } + } + + var end = classifications[classifications.Count - 1]; + if (imp == null) { + if (isSpace) { + return EmptyCompletionContext; + } + + // from [completion] + // or + // from xxx.[completion] + // or + // from xxx[Ctrl-Space completion] + return new ImportCompletionAnalysis(GetText(snapshot, start, end, true), loc.Start, span, buffer); + } + + // from xyz import [completion] + // or + // from xyz import abc[Ctrl-Space completion] + var nsText = GetText(snapshot, start, imp, false); + + string itemText; + if (Object.ReferenceEquals(end, imp)) { + itemText = String.Empty; + } else { + if (isSpace) { + return EmptyCompletionContext; + } + + var itemLen = end.Span.End - imp.Span.End - 1; + var itemSpan = new SnapshotSpan(snapshot, imp.Span.End + 1, itemLen); + itemText = itemSpan.GetText(); + } + + return new FromImportCompletionAnalysis(itemText, loc.Start, span, buffer, nsText); + } + + private static string GetText(ITextSnapshot snapshot, ClassificationSpan start, ClassificationSpan target, bool includeEnd) { + var nsLen = target.Span.Start - start.Span.End - (includeEnd ? 0 : 1); + var nsSpan = new SnapshotSpan(snapshot, start.Span.End + 1, nsLen); + var nsText = nsSpan.GetText().Trim(); + return nsText; + } + + public override CompletionSet GetCompletions(IGlyphService glyphService) { + var start = _stopwatch.ElapsedMilliseconds; + + string text = _namespace + "." + Text; + var completions = FromCompletions(glyphService, GetModules(glyphService, text)); + var res = new PythonCompletionSet(Text, Text, Span, completions, new Completion[0]); + + var end = _stopwatch.ElapsedMilliseconds; + + if (/*Logging &&*/ end - start > TooMuchTime) { + Trace.WriteLine(String.Format("{0} lookup time {1} for {2} imports", this, end - start, res.Completions.Count)); + } + + return res; + } + + private IEnumerable FromCompletions(IGlyphService glyphService, IEnumerable inputs) { + foreach (var input in inputs) { + yield return input; + } + + yield return PythonCompletion(glyphService, "*", "Import all members from the module", StandardGlyphGroup.GlyphArrow); + } + } + + class PythonCompletionSet : CompletionSet { + public PythonCompletionSet(string moniker, string displayName, ITrackingSpan applicableTo, IEnumerable completions, IEnumerable completionBuilders) : + base(moniker, displayName, applicableTo, completions, completionBuilders) { + } + + public override void SelectBestMatch() { + SelectBestMatch(CompletionMatchType.MatchInsertionText, false); + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/IPythonAnalyzer.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/IPythonAnalyzer.cs new file mode 100644 index 0000000000..ce669d352e --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/IPythonAnalyzer.cs @@ -0,0 +1,69 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using Microsoft.IronPythonTools.Intellisense; +using Microsoft.PyAnalysis; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.IronPythonTools.Intellisense { + + /// + /// Provides access to the Python analysis of files and text buffers. + /// + /// The analysis can be queried for information about expressions and new files can be added for the analysis to consider. + /// + public interface IPythonAnalyzer { + /// + /// Analyzes the specified text view and wires up support for tracking changes to the text view. + /// + IProjectEntry AnalyzeTextView(ITextView textView); + + /// + /// Analyzes the specified file on disk. + /// + IProjectEntry AnalyzeFile(string path); + + /// + /// Returns the project entry for the given file name or null if the file is not being analyzed. + /// + IProjectEntry GetAnalysisFromFile(string filename); + + /// + /// Gets a ExpressionAnalysis for the expression at the provided span. If the span is in + /// part of an identifier then the expression is extended to complete the identifier. + /// + ExpressionAnalysis AnalyzeExpression(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span); + + /// + /// Gets a list of signatuers available for the expression at the provided location in the snapshot. + /// + SignatureAnalysis GetSignatures(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span); + + /// + /// Gets a CompletionAnalysis providing a list of possible members the user can dot through. + /// + CompletionAnalysis GetCompletions(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span, bool intersectMembers = true, bool hideAdvancedMembers = false); + + /// + /// Returns true if there are currently items being analyzed. + /// + bool IsAnalyzing { get; } + + /// + /// Gets or sets whether the implicit project support is enabled and loose files shouldbe included. + /// + bool ImplicitProject { get; set; } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/ImportCompletionAnalysis.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/ImportCompletionAnalysis.cs new file mode 100644 index 0000000000..1a4b7a84c5 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/ImportCompletionAnalysis.cs @@ -0,0 +1,59 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; + +namespace Microsoft.IronPythonTools.Intellisense { + /// + /// Provides the completion context for when the user is doing an import + /// + internal class ImportCompletionAnalysis : CompletionAnalysis { + internal ImportCompletionAnalysis(string text, int pos, ITrackingSpan span, ITextBuffer textBuffer) + : base(text, pos, span, textBuffer) { + } + + public static CompletionAnalysis Make(ClassificationSpan start, ClassificationSpan end, Span loc, + ITextSnapshot snapshot, ITrackingSpan span, ITextBuffer buffer, bool isSpace) { + if (start == end) { + return new ImportCompletionAnalysis(String.Empty, loc.Start, span, buffer); + } else if (!isSpace) { + int nsLen = end.Span.End - start.Span.End - 1; + var nsSpan = new SnapshotSpan(snapshot, start.Span.End + 1, nsLen); + var text = nsSpan.GetText().Trim(); + return new ImportCompletionAnalysis(text, loc.Start, span, buffer); + } else { + return EmptyCompletionContext; + } + } + + public override CompletionSet GetCompletions(IGlyphService glyphService) { + var start = _stopwatch.ElapsedMilliseconds; + + var completions = GetModules(glyphService, Text); + var res = new PythonCompletionSet(Text, Text, Span, completions, new Completion[0]); + + var end = _stopwatch.ElapsedMilliseconds; + + if (/*Logging &&*/ end - start > TooMuchTime) { + Trace.WriteLine(String.Format("{0} lookup time {1} for {2} imports", this, end - start, res.Completions.Count)); + } + + return res; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/LazyCompletion.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/LazyCompletion.cs new file mode 100644 index 0000000000..1f5983116a --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/LazyCompletion.cs @@ -0,0 +1,55 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Windows.Media; +using Microsoft.VisualStudio.Language.Intellisense; + +namespace Microsoft.IronPythonTools.Intellisense { + internal class LazyCompletion : Completion { + private readonly string _displayText; + private readonly Func _insertionText, _description; + + public LazyCompletion(string displayText, Func insertionText, Func description, ImageSource glyph) { + Debug.Assert(displayText != null); + Debug.Assert(insertionText != null); + Debug.Assert(description != null); + + _displayText = displayText; + _insertionText = insertionText; + _description = description; + IconSource = glyph; + IconAutomationText = ""; + } + + public override string DisplayText { + get { + return _displayText; + } + } + + public override string InsertionText { + get { + return _insertionText(); + } + } + + public override string Description { + get { + return _description(); + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/NormalCompletionAnalysis.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/NormalCompletionAnalysis.cs new file mode 100644 index 0000000000..58e251e0e9 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/NormalCompletionAnalysis.cs @@ -0,0 +1,177 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Text; +using Microsoft.IronStudio.Repl; +using Microsoft.PyAnalysis; +using Microsoft.Scripting.Hosting; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronPythonTools.Intellisense { + internal class NormalCompletionAnalysis : CompletionAnalysis { + private readonly int _paramIndex; + private readonly ITextSnapshot _snapshot; + private readonly bool _intersectMembers, _hideAdvancedMembers; + + internal NormalCompletionAnalysis(string text, int pos, ITextSnapshot snapshot, ITrackingSpan span, ITextBuffer textBuffer, int paramIndex, bool intersectMembers = true, bool hideAdvancedMembers = false) + : base(text, pos, span, textBuffer) { + _paramIndex = paramIndex; + _snapshot = snapshot; + _intersectMembers = intersectMembers; + _hideAdvancedMembers = hideAdvancedMembers; + } + + public override CompletionSet GetCompletions(IGlyphService glyphService) { + var start1 = _stopwatch.ElapsedMilliseconds; + + MemberResult[] members = null; + IReplEvaluator eval; + IDlrEvaluator dlrEval; + if (_snapshot.TextBuffer.Properties.TryGetProperty(typeof(IReplEvaluator), out eval) && + (dlrEval = eval as IDlrEvaluator) != null) { + string text = Text; + if(Text.EndsWith(".")) { + text = Text.Substring(0, Text.Length - 1); + } + var memberNames = dlrEval.GetMemberNames(text); + + if (memberNames != null && memberNames.Count > 0) { + members = new MemberResult[memberNames.Count]; + int i = 0; + foreach(var member in memberNames) { + members[i++] = new MemberResult(member.Name, GetMemberType(member)); + } + } + } + + if (members == null) { + var analysis = GetAnalysisEntry(); + if (analysis != null) { + members = analysis.GetMembers( + Text, + _snapshot.GetLineNumberFromPosition(_pos) + 1, + _intersectMembers).ToArray(); + } else { + members = new MemberResult[0]; + } + } + + members = DoFilterCompletions(members); + Array.Sort(members, ModuleSort); + + var end = _stopwatch.ElapsedMilliseconds; + + if (/*Logging &&*/ (end - start1) > TooMuchTime) { + Trace.WriteLine(String.Format("{0} lookup time {1} for {2} members", this, end - start1, members.Length)); + } + + var start = _stopwatch.ElapsedMilliseconds; + + var result = new PythonCompletionSet( + Text, + Text, + _snapshot.CreateTrackingSpan(_pos, 0, SpanTrackingMode.EdgeInclusive), + TransformMembers(glyphService, members), + new Completion[0]); + + end = _stopwatch.ElapsedMilliseconds; + + if (/*Logging &&*/ (end - start1) > TooMuchTime) { + Trace.WriteLine(String.Format("{0} completion set time {1} total time {2}", this, end - start, end - start1)); + } + + return result; + } + + private IEnumerable TransformMembers(IGlyphService glyphService, MemberResult[] members) { + return members.Select(m => PythonCompletion(glyphService, m)); + } + + private MemberResult[] DoFilterCompletions(MemberResult[] members) { + if (_hideAdvancedMembers) { + members = FilterCompletions(members, Text, (completion, filter) => completion.StartsWith(filter) && (!completion.StartsWith("__") || ! completion.EndsWith("__"))); + } else { + members = FilterCompletions(members, Text, (x, y) => x.StartsWith(y)); + } + return members; + } + + private ResultType GetMemberType(MemberDoc member) { + switch (member.Kind) { + case MemberKind.Class: return ResultType.Class; + case MemberKind.Constant: return ResultType.Constant; + case MemberKind.Delegate: return ResultType.Delegate; + case MemberKind.Enum: return ResultType.Enum; + case MemberKind.EnumMember: return ResultType.EnumInstance; + case MemberKind.Event: return ResultType.Event; + case MemberKind.Field: return ResultType.Field; + case MemberKind.Function: return ResultType.Function; + case MemberKind.Instance: return ResultType.Instance; + case MemberKind.Method: return ResultType.Method; + case MemberKind.Module: return ResultType.Module; + case MemberKind.Namespace: return ResultType.Namespace; + case MemberKind.Property: return ResultType.Property; + default: + return ResultType.Unknown; + } + } + + internal static MemberResult[] FilterCompletions(MemberResult[] completions, string text, Func filterFunc) { + var cut = text.LastIndexOfAny(new[] { '.', ']', ')' }); + var filter = (cut == -1) ? text : text.Substring(cut + 1); + + var result = new List(completions.Length); + foreach (var comp in completions) { + if (filterFunc(comp.Name, filter)) { + result.Add(new MemberResult(comp.Name, comp.Name.Substring(filter.Length), comp.Namespaces)); + } + } + return result.ToArray(); + } + + internal static int ModuleSort(MemberResult x, MemberResult y) { + return MemberSortComparison(x.Name, y.Name); + } + + /// + /// Sorts members for displaying in completion list. The member sort + /// moves all __x__ functions to the end of the list. Members which + /// start with a single underscore (private members) are sorted as if + /// they did not start with an underscore. + /// + internal static int MemberSortComparison(string xName, string yName) { + bool xUnder = xName.StartsWith("__") && xName.EndsWith("__"); + bool yUnder = yName.StartsWith("__") && yName.EndsWith("__"); + int xStart = 0, yStart = 0; + if (!xUnder && xName.StartsWith("_")) { + xStart = 1; + } + if (!yUnder && yName.StartsWith("_")) { + yStart = 1; + } + if (xUnder == yUnder) { + // Compare the actual strings + return String.Compare(xName, xStart, yName, yStart, Math.Max(xName.Length, yName.Length), StringComparison.OrdinalIgnoreCase); + } + // The one that starts with an underscore comes later + return xUnder ? 1 : -1; + } + + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/PythonAnalyzer.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/PythonAnalyzer.cs new file mode 100644 index 0000000000..f788a3e378 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/PythonAnalyzer.cs @@ -0,0 +1,618 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Diagnostics; +using System.IO; +using System.Threading; +using IronPython; +using IronPython.Compiler; +using IronPython.Compiler.Ast; +using Microsoft.IronPythonTools.Internal; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Intellisense; +using Microsoft.IronStudio.Library.Intellisense; +using Microsoft.IronStudio.Repl; +using Microsoft.PyAnalysis; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Hosting.Providers; +using Microsoft.Scripting.Library; +using Microsoft.Scripting.Runtime; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Adornments; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; + +namespace Microsoft.IronPythonTools.Intellisense { + /// + /// Performs centralized parsing and analysis of Python source code. + /// + [Export(typeof(IPythonAnalyzer))] + internal class PythonAnalyzer : IParser, IAnalyzer, IPythonAnalyzer { + private readonly ParseQueue _queue; + private readonly AnalysisQueue _analysisQueue; + private readonly ScriptEngine _engine; + private readonly IErrorProviderFactory _squiggleProvider; + private readonly Dictionary _projectFiles; + private readonly ProjectState _analysisState; + private bool _implicitProject = true; + + private static PythonOptions EmptyOptions = new PythonOptions(); + + [ImportingConstructor] + public PythonAnalyzer(IPythonRuntimeHost runtimeHost, IErrorProviderFactory errorProvider) { + _engine = runtimeHost.ScriptEngine; + _squiggleProvider = errorProvider; + + _queue = new ParseQueue(this); + _analysisQueue = new AnalysisQueue(this); + _analysisState = new ProjectState(_engine); + _projectFiles = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + #region IPythonAnalyzer + + public IProjectEntry AnalyzeTextView(ITextView textView) { + // Get an AnalysisItem for this file, creating one if necessary + var res = textView.TextBuffer.Properties.GetOrCreateSingletonProperty(() => { + string path = textView.GetFilePath(); + if (path == null) { + return null; + } + + IProjectEntry entry; + if (!_projectFiles.TryGetValue(path, out entry)) { + var modName = PathToModuleName(path); + + var initialSnapshot = textView.TextBuffer.CurrentSnapshot; + + if (textView.TextBuffer.ContentType.IsOfType(PythonCoreConstants.ContentType)) { + entry = _analysisState.AddModule( + modName, + textView.GetFilePath(), + new SnapshotCookie(initialSnapshot) + ); + } else if (textView.TextBuffer.ContentType.IsOfType("xaml")) { + entry = _analysisState.AddXamlFile(path); + } else { + return null; + } + + _projectFiles[path] = entry; + + if (ImplicitProject) { + AddImplicitFiles(Path.GetDirectoryName(Path.GetFullPath(path))); + } + } + + return entry; + }); + + // kick off initial processing on the ITextWindow + _queue.EnqueueBuffer(textView); + + return res; + } + + public IProjectEntry AnalyzeFile(string path) { + IProjectEntry item; + if (!_projectFiles.TryGetValue(path, out item)) { + if (path.EndsWith(".py", StringComparison.OrdinalIgnoreCase)) { + var modName = PathToModuleName(path); + + item = _analysisState.AddModule( + modName, + path, + null + ); + } else if (path.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase)) { + item = _analysisState.AddXamlFile(path); + } + + if (item != null) { + _projectFiles[path] = item; + } + } + + _queue.EnqueueFile(path); + + return item; + } + + public IProjectEntry GetAnalysisFromFile(string path) { + IProjectEntry res; + if (_projectFiles.TryGetValue(path, out res)) { + return res; + } + return null; + } + + /// + /// Gets a ExpressionAnalysis for the expression at the provided span. If the span is in + /// part of an identifier then the expression is extended to complete the identifier. + /// + public ExpressionAnalysis AnalyzeExpression(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span) { + ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, buffer, span); + + var loc = parser.Span.GetSpan(parser.Snapshot.Version); + var exprRange = parser.GetExpressionRange(); + if (exprRange == null) { + return ExpressionAnalysis.Empty; + } + + // extend right for any partial expression the user is hovering on, for example: + // "x.Baz" where the user is hovering over the B in baz we want the complete + // expression. + var text = exprRange.Value.GetText(); + var endingLine = exprRange.Value.End.GetContainingLine(); + if (endingLine.End.Position - exprRange.Value.End.Position < 0) { + return ExpressionAnalysis.Empty; + } + var endText = snapshot.GetText(exprRange.Value.End.Position, endingLine.End.Position - exprRange.Value.End.Position); + bool allChars = true; + for (int i = 0; i < endText.Length; i++) { + if (!Char.IsLetterOrDigit(endText[i]) && endText[i] != '_') { + text += endText.Substring(0, i); + allChars = false; + break; + } + } + if (allChars) { + text += endText; + } + + var applicableSpan = parser.Snapshot.CreateTrackingSpan( + exprRange.Value.Span, + SpanTrackingMode.EdgeExclusive + ); + + IProjectEntry analysisItem; + if (buffer.TryGetAnalysis(out analysisItem)) { + var analysis = ((IPythonProjectEntry)analysisItem).Analysis; + if (analysis != null && text.Length > 0) { + + var lineNo = parser.Snapshot.GetLineNumberFromPosition(loc.Start); + return new ExpressionAnalysis( + text, + analysis, + lineNo + 1, + applicableSpan); + } + } + + return ExpressionAnalysis.Empty; + } + + /// + /// Gets a CompletionList providing a list of possible members the user can dot through. + /// + public CompletionAnalysis GetCompletions(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span, bool intersectMembers = true, bool hideAdvancedMembers = false) { + ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, buffer, span); + + var loc = parser.Span.GetSpan(parser.Snapshot.Version); + var line = parser.Snapshot.GetLineFromPosition(loc.Start); + var lineStart = line.Start; + + var textLen = loc.End - lineStart.Position; + if (textLen <= 0) { + // Ctrl-Space on an empty line, we just want to get global vars + return new NormalCompletionAnalysis(String.Empty, loc.Start, parser.Snapshot, parser.Span, parser.Buffer, 0); + } + + return TrySpecialCompletions(parser, loc) ?? + GetNormalCompletionContext(parser, loc, intersectMembers, hideAdvancedMembers); + } + + /// + /// Gets a list of signatuers available for the expression at the provided location in the snapshot. + /// + public SignatureAnalysis GetSignatures(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span) { + ReverseExpressionParser parser = new ReverseExpressionParser(snapshot, buffer, span); + + var loc = parser.Span.GetSpan(parser.Snapshot.Version); + + int paramIndex; + SnapshotPoint? sigStart; + var exprRange = parser.GetExpressionRange(1, out paramIndex, out sigStart); + if (exprRange == null || sigStart == null) { + return new SignatureAnalysis("", 0, new ISignature[0]); + } + + Debug.Assert(sigStart != null); + var text = new SnapshotSpan(exprRange.Value.Snapshot, new Span(exprRange.Value.Start, sigStart.Value.Position - exprRange.Value.Start)).GetText(); + //var text = exprRange.Value.GetText(); + var applicableSpan = parser.Snapshot.CreateTrackingSpan(exprRange.Value.Span, SpanTrackingMode.EdgeInclusive); + + var liveSigs = TryGetLiveSignatures(snapshot, paramIndex, text, applicableSpan); + if (liveSigs != null) { + return liveSigs; + } + + var start = Stopwatch.ElapsedMilliseconds; + + var analysisItem = buffer.GetAnalysis(); + if (analysisItem != null) { + var analysis = ((IPythonProjectEntry)analysisItem).Analysis; + if (analysis != null) { + + var lineNo = parser.Snapshot.GetLineNumberFromPosition(loc.Start); + + var sigs = analysis.GetSignatures(text, lineNo + 1); + var end = Stopwatch.ElapsedMilliseconds; + + if (/*Logging &&*/ (end - start) > CompletionAnalysis.TooMuchTime) { + Trace.WriteLine(String.Format("{0} lookup time {1} for signatures", text, end - start)); + } + + var result = new List(); + foreach (var sig in sigs) { + result.Add(new PythonSignature(applicableSpan, sig, paramIndex)); + } + + return new SignatureAnalysis( + text, + paramIndex, + result + ); + } + } + return new SignatureAnalysis(text, paramIndex, new ISignature[0]); + } + + public bool IsAnalyzing { + get { + return _queue.IsParsing || _analysisQueue.IsAnalyzing; + } + } + + public bool ImplicitProject { + get { + return _implicitProject; + } + set { + _implicitProject = value; + } + } + + #endregion + + #region IParser Members + + public void Parse(TextContentProvider content) { + + ISnapshotTextContentProvider snapshotContent = content as ISnapshotTextContentProvider; + if (snapshotContent != null) { + ParseSnapshot(snapshotContent); + } else { + FileTextContentProvider fileContent = content as FileTextContentProvider; + if (fileContent != null) { + ParseFile(fileContent); + } + } + + } + + private void ParseFile(FileTextContentProvider fileContent) { + if (fileContent.Path.EndsWith(".py", StringComparison.OrdinalIgnoreCase)) { + PythonAst ast; + CollectingErrorSink errorSink; + ParsePythonCode(fileContent, out ast, out errorSink); + + if (ast != null) { + IProjectEntry analysis; + IPythonProjectEntry pyAnalysis; + if (fileContent != null && + _projectFiles.TryGetValue(fileContent.Path, out analysis) && + (pyAnalysis = analysis as IPythonProjectEntry) != null) { + + pyAnalysis.UpdateTree(ast, new FileCookie(fileContent.Path)); + _analysisQueue.Enqueue(analysis, AnalysisPriority.Normal); + } + } + } else if (fileContent.Path.EndsWith(".xaml", StringComparison.OrdinalIgnoreCase)) { + IProjectEntry analysis; + XamlProjectEntry xamlProject; + if (_projectFiles.TryGetValue(fileContent.Path, out analysis) && + (xamlProject = analysis as XamlProjectEntry) != null) { + xamlProject.UpdateContent(fileContent.GetReader(), new FileCookie(fileContent.Path)); + _analysisQueue.Enqueue(analysis, AnalysisPriority.Normal); + } + } + } + + private void ParseSnapshot(ISnapshotTextContentProvider snapshotContent) { + + // queue analysis of the parsed tree at High Pri so the active buffer is quickly re-analyzed + var snapshot = snapshotContent.Snapshot; + + if (snapshot.TextBuffer.ContentType.IsOfType(PythonCoreConstants.ContentType)) { + PythonAst ast; + CollectingErrorSink errorSink; + ParsePythonCode((TextContentProvider)snapshotContent, out ast, out errorSink); + if (ast != null) { + IPythonProjectEntry analysis; + if (snapshot.TextBuffer.TryGetPythonAnalysis(out analysis)) { + // only update the AST when we're error free, this way we don't remove + // a useful analysis with an incomplete and useless analysis. + if (errorSink.Errors.Count == 0) { + analysis.UpdateTree(ast, new SnapshotCookie(snapshot)); + _analysisQueue.Enqueue(analysis, AnalysisPriority.High); + } + + SimpleTagger squiggles = _squiggleProvider.GetErrorTagger(snapshot.TextBuffer); + squiggles.RemoveTagSpans(x => true); + + // update squiggles for the live buffer + foreach (ErrorResult warning in errorSink.Warnings) { + var span = warning.Span; + var tspan = CreateSpan(snapshot, span); + squiggles.CreateTagSpan(tspan, new ErrorTag("Warning", warning.Message)); + } + + foreach (ErrorResult error in errorSink.Errors) { + var span = error.Span; + var tspan = CreateSpan(snapshot, span); + squiggles.CreateTagSpan(tspan, new ErrorTag("Error", error.Message)); + } + } + } + } else if (snapshot.TextBuffer.ContentType.IsOfType("xaml")) { + string path = snapshot.TextBuffer.GetFilePath(); + if (path != null) { + IProjectEntry analysis; + XamlProjectEntry xamlProject; + if (_projectFiles.TryGetValue(path, out analysis) && + (xamlProject = analysis as XamlProjectEntry) != null) { + xamlProject.UpdateContent(((TextContentProvider)snapshotContent).GetReader(), new SnapshotCookie(snapshotContent.Snapshot)); + _analysisQueue.Enqueue(analysis, AnalysisPriority.High); + } + } + + } + } + + private void ParsePythonCode(TextContentProvider content, out PythonAst ast, out CollectingErrorSink errorSink) { + ast = null; + errorSink = new CollectingErrorSink(); + + // parse the tree + var source = _engine.CreateScriptSource(content, "", SourceCodeKind.File); + var compOptions = (PythonCompilerOptions)HostingHelpers.GetLanguageContext(_engine).GetCompilerOptions(); + var context = new CompilerContext(HostingHelpers.GetSourceUnit(source), compOptions, errorSink); + //compOptions.Verbatim = true; + using (var parser = MakeParser(context)) { + if (parser != null) { + try { + ast = parser.ParseFile(false); + } catch (Exception e) { + Debug.Assert(false, String.Format("Failure in IronPython parser: {0}", e.ToString())); + } + + } + } + } + + private static Parser MakeParser(CompilerContext context) { + for (int i = 0; i < 10; i++) { + try { + return Parser.CreateParser(context, EmptyOptions); + } catch (IOException) { + // file being copied, try again... + Thread.Sleep(100); + } + } + return null; + } + + private static ITrackingSpan CreateSpan(ITextSnapshot snapshot, SourceSpan span) { + var tspan = snapshot.CreateTrackingSpan( + new Span( + span.Start.Index, + Math.Min(span.End.Index - span.Start.Index, Math.Max(snapshot.Length - span.Start.Index, 0)) + ), + SpanTrackingMode.EdgeInclusive + ); + return tspan; + } + + #endregion + + #region IAnalyzer Members + + public void Analyze(IProjectEntry content) { + content.Analyze(); + } + + #endregion + + #region Implementation Details + + private static Stopwatch _stopwatch = MakeStopWatch(); + + internal static Stopwatch Stopwatch { + get { + return _stopwatch; + } + } + + private static SignatureAnalysis TryGetLiveSignatures(ITextSnapshot snapshot, int paramIndex, string text, ITrackingSpan applicableSpan) { + IReplEvaluator eval; + IDlrEvaluator dlrEval; + if (snapshot.TextBuffer.Properties.TryGetProperty(typeof(IReplEvaluator), out eval) && + (dlrEval = eval as IDlrEvaluator) != null) { + if (text.EndsWith("(")) { + text = text.Substring(0, text.Length - 1); + } + var liveSigs = dlrEval.GetSignatureDocumentation(text); + + if (liveSigs != null && liveSigs.Count > 0) { + return new SignatureAnalysis(text, paramIndex, GetLiveSignatures(text, liveSigs, paramIndex, applicableSpan)); + } + } + return null; + } + + private static ISignature[] GetLiveSignatures(string text, ICollection liveSigs, int paramIndex, ITrackingSpan span) { + ISignature[] res = new ISignature[liveSigs.Count]; + int i = 0; + foreach (var sig in liveSigs) { + var parameters = new ParameterResult[sig.Parameters.Count]; + int j = 0; + foreach (var param in sig.Parameters) { + parameters[j++] = new ParameterResult(param.Name); + } + + res[i++] = new PythonSignature( + span, + new LiveOverloadResult(text, sig.Documentation, parameters), + paramIndex + ); + } + return res; + } + + class LiveOverloadResult : IOverloadResult { + private readonly string _name, _doc; + private readonly ParameterResult[] _parameters; + + public LiveOverloadResult(string name, string documentation, ParameterResult[] parameters) { + _name = name; + _doc = documentation; + _parameters = parameters; + } + + #region IOverloadResult Members + + public string Name { + get { return _name; } + } + + public string Documentation { + get { return _doc; } + } + + public ParameterResult[] Parameters { + get { return _parameters; } + } + + #endregion + } + + private static CompletionAnalysis TrySpecialCompletions(ReverseExpressionParser parser, Span loc) { + if (parser.Tokens.Count > 0) { + // Check for context-sensitive intellisense + var lastClass = parser.Tokens[parser.Tokens.Count - 1]; + + if (lastClass.ClassificationType == parser.Classifier.Provider.Comment) { + // No completions in comments + return CompletionAnalysis.EmptyCompletionContext; + } else if (lastClass.ClassificationType == parser.Classifier.Provider.StringLiteral) { + // String completion + return new StringLiteralCompletionList(lastClass.Span.GetText(), loc.Start, parser.Span, parser.Buffer); + } + + // Import completions + var first = parser.Tokens[0]; + if (CompletionAnalysis.IsKeyword(first, "import")) { + return ImportCompletionAnalysis.Make(first, lastClass, loc, parser.Snapshot, parser.Span, parser.Buffer, IsSpaceCompletion(parser, loc)); + } else if (CompletionAnalysis.IsKeyword(first, "from")) { + return FromImportCompletionAnalysis.Make(parser.Tokens, first, loc, parser.Snapshot, parser.Span, parser.Buffer, IsSpaceCompletion(parser, loc)); + } + return null; + } + + return CompletionAnalysis.EmptyCompletionContext; + } + + private static CompletionAnalysis GetNormalCompletionContext(ReverseExpressionParser parser, Span loc, bool intersectMembers = true, bool hideAdvancedMembers = false) { + var exprRange = parser.GetExpressionRange(); + if (exprRange == null) { + return CompletionAnalysis.EmptyCompletionContext; + } + if (IsSpaceCompletion(parser, loc)) { + return CompletionAnalysis.EmptyCompletionContext; + } + + var text = exprRange.Value.GetText(); + + var applicableSpan = parser.Snapshot.CreateTrackingSpan( + exprRange.Value.Span, + SpanTrackingMode.EdgeExclusive + ); + + return new NormalCompletionAnalysis( + text, + loc.Start, + parser.Snapshot, + applicableSpan, + parser.Buffer, + -1, + intersectMembers, + hideAdvancedMembers + ); + } + + private static bool IsSpaceCompletion(ReverseExpressionParser parser, Span loc) { + var keySpan = new SnapshotSpan(parser.Snapshot, loc.Start - 1, 1); + return (keySpan.GetText() == " "); + } + + private static Stopwatch MakeStopWatch() { + var res = new Stopwatch(); + res.Start(); + return res; + } + + private void AddImplicitFiles(string dir) { + foreach (string filename in Directory.GetFiles(dir, "*.py")) { + AnalyzeFile(filename); + } + + foreach (string innerDir in Directory.GetDirectories(dir)) { + if (File.Exists(Path.Combine(innerDir, "__init__.py"))) { + AddImplicitFiles(innerDir); + } + } + } + + internal static string PathToModuleName(string path) { + string moduleName; + string dirName; + + if (path == null) { + return String.Empty; + } else if (path.EndsWith("__init__.py")) { + moduleName = Path.GetFileName(Path.GetDirectoryName(path)); + dirName = Path.GetDirectoryName(path); + } else { + moduleName = Path.GetFileNameWithoutExtension(path); + dirName = path; + } + + while (dirName.Length != 0 && (dirName = Path.GetDirectoryName(dirName)).Length != 0 && + File.Exists(Path.Combine(dirName, "__init__.py"))) { + moduleName = Path.GetFileName(dirName) + "." + moduleName; + } + + return moduleName; + } + + #endregion + + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/PythonParameter.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/PythonParameter.cs new file mode 100644 index 0000000000..df859d035b --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/PythonParameter.cs @@ -0,0 +1,52 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using Microsoft.PyAnalysis; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; + + +namespace Microsoft.IronPythonTools.Intellisense { + internal class PythonParameter : IParameter { + private readonly ISignature _signature; + private readonly ParameterResult _param; + private readonly Span _locus; + + public PythonParameter(ISignature signature, ParameterResult param, Span locus) { + _signature = signature; + _param = param; + _locus = locus; + } + + public string Documentation { + get { return _param.Documentation; } + } + + public Span Locus { + get { return _locus; } + } + + public string Name { + get { return _param.Name; } + } + + public ISignature Signature { + get { return _signature; } + } + + public Span PrettyPrintedLocus { + get { return Locus; } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/PythonSignature.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/PythonSignature.cs new file mode 100644 index 0000000000..410a18ba2f --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/PythonSignature.cs @@ -0,0 +1,116 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.ObjectModel; +using System.Text; +using Microsoft.PyAnalysis; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; + + +namespace Microsoft.IronPythonTools.Intellisense { + internal class PythonSignature : ISignature { + private readonly ITrackingSpan _span; + private readonly string _content; + private readonly ReadOnlyCollection _parameters; + private IParameter _currentParameter; + private readonly IOverloadResult _overload; + + public PythonSignature(ITrackingSpan span, IOverloadResult overload, int paramIndex) { + _span = span; + _overload = overload; + + var content = new StringBuilder(overload.Name); + content.Append('('); + int start = content.Length; + var parameters = new IParameter[overload.Parameters.Length]; + for (int i = 0; i < overload.Parameters.Length; i++) { + var param = overload.Parameters[i]; + if (param.IsOptional) { + content.Append("["); + } + if (i > 0) { + content.Append(", "); + start = content.Length; + } + + content.Append(param.Name); + if (param.Type != null && param.Type != "object") { + content.Append(": "); + content.Append(param.Type); + } + + var paramSpan = new Span(start, content.Length - start); + + if (param.IsOptional) { + content.Append("]"); + } + + parameters[i] = new PythonParameter(this, param, paramSpan); + } + content.Append(')'); + _content = content.ToString(); + + _parameters = new ReadOnlyCollection(parameters); + if (paramIndex < parameters.Length) { + _currentParameter = parameters[paramIndex]; + } else { + _currentParameter = null; + } + } + + internal void SetCurrentParameter(IParameter newValue) { + if (newValue != _currentParameter) { + var args = new CurrentParameterChangedEventArgs(_currentParameter, newValue); + _currentParameter = newValue; + var changed = CurrentParameterChanged; + if (changed != null) { + changed(this, args); + } + } + } + + public ITrackingSpan ApplicableToSpan { + get { return _span; } + } + + public string Content { + get { return _content; } + } + + public IParameter CurrentParameter { + get { return _currentParameter; } + } + + public event EventHandler CurrentParameterChanged; + + public string Documentation { + get { return _overload.Documentation; } + } + + public ReadOnlyCollection Parameters { + get { return _parameters; } + } + + #region ISignature Members + + + public string PrettyPrintedContent { + get { return Content; } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/QuickInfoSource.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/QuickInfoSource.cs new file mode 100644 index 0000000000..b92447c3a3 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/QuickInfoSource.cs @@ -0,0 +1,98 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Text; +using Microsoft.IronPythonTools.Internal; +using Microsoft.PyAnalysis; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronPythonTools.Intellisense { + internal class QuickInfoSource : IQuickInfoSource { + private readonly ITextBuffer _textBuffer; + private readonly QuickInfoSourceProvider _provider; + + public QuickInfoSource(QuickInfoSourceProvider provider, ITextBuffer textBuffer) { + _textBuffer = textBuffer; + _provider = provider; + } + + #region IQuickInfoSource Members + + public void AugmentQuickInfoSession(IQuickInfoSession session, System.Collections.Generic.IList quickInfoContent, out ITrackingSpan applicableToSpan) { + var textBuffer = session.TextView.TextBuffer; + + var vars = _provider._Analyzer.AnalyzeExpression( + textBuffer.CurrentSnapshot, + textBuffer, + session.CreateTrackingSpan(textBuffer) + ); + + applicableToSpan = vars.Span; + if (String.IsNullOrEmpty(vars.Expression)) { + return; + } + + bool first = true; + var result = new StringBuilder(); + int count = 0; + List listVars = new List(vars.Values); + HashSet descriptions = new HashSet(); + bool multiline = false; + foreach (var v in listVars) { + string description = null; + if (listVars.Count == 1) { + if (v.Description != null) { + description = v.Description; + } + } else { + if (v.ShortDescription != null) { + description = v.ShortDescription; + } + } + + if (descriptions.Add(description)) { + if (first) { + first = false; + } else { + if (result.Length == 0 || result[result.Length - 1] != '\n') { + result.Append(", "); + } else { + multiline = true; + } + } + result.Append(description); + count++; + } + } + + if (multiline) { + result.Insert(0, vars.Expression + ": " + Environment.NewLine); + } else { + result.Insert(0, vars.Expression + ": "); + } + + quickInfoContent.Add(result.ToString()); + } + + #endregion + + public void Dispose() { + } + + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/QuickInfoSourceProvider.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/QuickInfoSourceProvider.cs new file mode 100644 index 0000000000..abb4be6c36 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/QuickInfoSourceProvider.cs @@ -0,0 +1,31 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel.Composition; +using Microsoft.IronPythonTools.Library; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronPythonTools.Intellisense { + [Export(typeof(IQuickInfoSourceProvider)), ContentType(PythonCoreConstants.ContentType), Order, Name("Python Quick Info Source")] + class QuickInfoSourceProvider : IQuickInfoSourceProvider { + [Import(typeof(IPythonAnalyzer))] + internal IPythonAnalyzer _Analyzer = null; + + public IQuickInfoSource TryCreateQuickInfoSource(ITextBuffer textBuffer) { + return new QuickInfoSource(this, textBuffer); + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/ReverseExpressionParser.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/ReverseExpressionParser.cs new file mode 100644 index 0000000000..0eff7fbf32 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/ReverseExpressionParser.cs @@ -0,0 +1,172 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.IronStudio; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; + +namespace Microsoft.IronPythonTools.Intellisense { + /// + /// Parses an expression in reverse to get the experssion we need to + /// analyze for completion, quick info, or signature help. + /// + class ReverseExpressionParser { + private readonly ITextSnapshot _snapshot; + private readonly ITextBuffer _buffer; + private readonly ITrackingSpan _span; + private IList _tokens; + private ITextSnapshotLine _curLine; + private IDlrClassifier _classifier; + + public ReverseExpressionParser(ITextSnapshot snapshot, ITextBuffer buffer, ITrackingSpan span) { + _snapshot = snapshot; + _buffer = buffer; + _span = span; + + var loc = span.GetSpan(snapshot); + var line = _curLine = snapshot.GetLineFromPosition(loc.Start); + + var targetSpan = new Span(line.Start.Position, span.GetEndPoint(snapshot).Position - line.Start.Position); + _tokens = Classifier.GetClassificationSpans(new SnapshotSpan(snapshot, targetSpan)); + } + + public SnapshotSpan? GetExpressionRange() { + int dummy; + SnapshotPoint? dummyPoint; + return GetExpressionRange(0, out dummy, out dummyPoint); + } + + /// + /// Gets the range of the expression to the left of our starting span. + /// + /// 1 if we have an opening parenthesis for sig completion + /// The current parameter index. + /// + public SnapshotSpan? GetExpressionRange(int nesting, out int paramIndex, out SnapshotPoint? sigStart) { + SnapshotSpan? start = null; + var endText = String.Empty; + paramIndex = 0; + sigStart = null; + bool nestingChanged = false; + + ClassificationSpan lastToken = null; + // Walks backwards over all the lines + if (Tokens.Count > 0) { + lastToken = Tokens[Tokens.Count - 1]; + while (true) { + // Walk backwards over the tokens in the current line + for (int t = Tokens.Count - 1; t >= 0; t--) { + var token = Tokens[t]; + var text = token.Span.GetText(); + + if (token.ClassificationType == Classifier.Provider.Keyword || + (token.ClassificationType == Classifier.Provider.Operator && + text != "[" && text != "]" && text != "}" && text != "{")) { + if (nesting == 0) { + if (start == null) { + // hovering directly over a keyword, don't provide a tooltip + return null; + } else if (nestingChanged && token.ClassificationType == Classifier.Provider.Keyword && text == "def") { + return null; + } + break; + } + } else if (token.ClassificationType == Classifier.Provider.OpenGroupingClassification || text == "[" || text == "{") { + if (nesting != 0) { + nesting--; + nestingChanged = true; + if (nesting == 0 && sigStart == null) { + sigStart = token.Span.Start; + } + } else { + break; + } + } else if (token.ClassificationType == Classifier.Provider.CloseGroupingClassification || + text == "]" || text == "}") { + nesting++; + nestingChanged = true; + } else if (token.ClassificationType == Classifier.Provider.CommaClassification) { + if (nesting == 0) { + if (start == null) { + return null; + } + break; + } else if (nesting == 1 && sigStart == null) { + paramIndex++; + } + } + + start = token.Span; + } + + if (nesting == 0 || CurrentLine.LineNumber == 0) { + break; + } + + // We're in a nested paren context, continue to the next line + // to capture the entire expression + endText = CurrentLine.GetText() + endText; + CurrentLine = Snapshot.GetLineFromLineNumber(CurrentLine.LineNumber - 1); + + var classSpan = new SnapshotSpan(Snapshot, CurrentLine.Start, CurrentLine.Length); + Tokens = Classifier.GetClassificationSpans(classSpan); + } + } + + if (start.HasValue) { + return new SnapshotSpan( + Snapshot, + new Span( + start.Value.Start.Position, + //_span.GetEndPoint(_snapshot).Position - start.Value.Start.Position + lastToken.Span.End.Position - start.Value.Start.Position + ) + ); + } + + return _span.GetSpan(_snapshot); + } + + public IDlrClassifier Classifier { + get { return _classifier ?? (_classifier = (IDlrClassifier)_buffer.Properties.GetProperty(typeof(IDlrClassifier))); } + } + + public ITextSnapshot Snapshot { + get { return _snapshot; } + } + + public ITextBuffer Buffer { + get { return _buffer; } + } + + public ITrackingSpan Span { + get { return _span; } + } + + /// + /// Tokens for the current line + /// + public IList Tokens { + get { return _tokens; } + set { _tokens = value; } + } + + public ITextSnapshotLine CurrentLine { + get { return _curLine; } + set { _curLine = value; } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/SignatureAnalysis.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/SignatureAnalysis.cs new file mode 100644 index 0000000000..80a9b3a1db --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/SignatureAnalysis.cs @@ -0,0 +1,51 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio.Language.Intellisense; + +namespace Microsoft.IronPythonTools.Intellisense { + public class SignatureAnalysis { + private readonly string _text; + private readonly int _paramIndex; + private readonly ISignature[] _signatures; + + internal SignatureAnalysis(string text, int paramIndex, IList signatures) { + _text = text; + _paramIndex = paramIndex; + _signatures = new ISignature[signatures.Count]; + signatures.CopyTo(_signatures, 0); + Array.Sort(_signatures, (x, y) => x.Parameters.Count - y.Parameters.Count); + } + + public string Text { + get { + return _text; + } + } + + public int ParameterIndex { + get { + return _paramIndex; + } + } + + public IList Signatures { + get { + return _signatures; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/SignatureHelpSource.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/SignatureHelpSource.cs new file mode 100644 index 0000000000..88f8eb1baa --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/SignatureHelpSource.cs @@ -0,0 +1,64 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.ObjectModel; +using Microsoft.IronPythonTools.Internal; +using Microsoft.IronStudio; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronPythonTools.Intellisense { + internal class SignatureHelpSource : ISignatureHelpSource { + private readonly ITextBuffer _textBuffer; + private readonly SignatureHelpSourceProvider _provider; + + public SignatureHelpSource(SignatureHelpSourceProvider provider, ITextBuffer textBuffer) { + _textBuffer = textBuffer; + _provider = provider; + } + + public ISignature GetBestMatch(ISignatureHelpSession session) { + return null; + } + + public void AugmentSignatureHelpSession(ISignatureHelpSession session, System.Collections.Generic.IList signatures) { + var textBuffer = session.TextView.TextBuffer; + var span = session.CreateTrackingSpan(textBuffer); + + var sigs = _provider._Analysis.GetSignatures(textBuffer.CurrentSnapshot, textBuffer, span); + + ISignature curSig = null; + + foreach (var sig in sigs.Signatures) { + if (sigs.ParameterIndex == 0 || sig.Parameters.Count > sigs.ParameterIndex) { + curSig = sig; + break; + } + } + + foreach (var sig in sigs.Signatures) { + signatures.Add(sig); + } + + if (curSig != null) { + // save the current sig so we don't need to recalculate it (we can't set it until + // the signatures are added by our caller). + session.Properties.AddProperty(typeof(PythonSignature), curSig); + } + } + + public void Dispose() { + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/SignatureHelpSourceProvider.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/SignatureHelpSourceProvider.cs new file mode 100644 index 0000000000..e55ea13157 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/SignatureHelpSourceProvider.cs @@ -0,0 +1,30 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel.Composition; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronPythonTools.Intellisense { + [Export(typeof(ISignatureHelpSourceProvider)), ContentType(PythonCoreConstants.ContentType), Order, Name("Python Signature Help Source")] + class SignatureHelpSourceProvider : ISignatureHelpSourceProvider { + [Import(typeof(IPythonAnalyzer))] + internal IPythonAnalyzer _Analysis = null; + + public ISignatureHelpSource TryCreateSignatureHelpSource(ITextBuffer textBuffer) { + return new SignatureHelpSource(this, textBuffer); + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/SnapshotCookie.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/SnapshotCookie.cs new file mode 100644 index 0000000000..9a890b604b --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/SnapshotCookie.cs @@ -0,0 +1,45 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.PyAnalysis; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronPythonTools.Intellisense { + class SnapshotCookie : IAnalysisCookie { + private readonly ITextSnapshot _snapshot; + + public SnapshotCookie(ITextSnapshot snapshot) { + _snapshot = snapshot; + } + + public ITextSnapshot Snapshot { + get { + return _snapshot; + } + } + + #region IFileCookie Members + + public string GetLine(int lineNo) { + return _snapshot.GetLineFromLineNumber(lineNo - 1).GetText(); + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/StringLiteralCompletionList.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/StringLiteralCompletionList.cs new file mode 100644 index 0000000000..9755484c6e --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Intellisense/StringLiteralCompletionList.cs @@ -0,0 +1,32 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronPythonTools.Intellisense { + /// + /// Provides file path completion + /// + internal class StringLiteralCompletionList : CompletionAnalysis { + internal StringLiteralCompletionList(string text, int pos, ITrackingSpan span, ITextBuffer textBuffer) + : base(text, pos, span, textBuffer) { + } + + public override CompletionSet GetCompletions(IGlyphService glyphService) { + // TODO: implement + return null; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Navigation/AstScopeNode.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Navigation/AstScopeNode.cs new file mode 100644 index 0000000000..2540b62c42 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Navigation/AstScopeNode.cs @@ -0,0 +1,78 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using Microsoft.IronStudio.Navigation; +using Microsoft.PyAnalysis; +using Microsoft.Scripting; + +namespace Microsoft.IronPythonTools.Navigation { + class AstScopeNode : IScopeNode { + private readonly PythonAst _ast; + private readonly IPythonProjectEntry _projectEntry; + + public AstScopeNode(PythonAst pythonAst, IPythonProjectEntry projectEntry) { + _ast = pythonAst; + _projectEntry = projectEntry; + } + + #region IScopeNode Members + + public bool IsFunction { + get { return false; } + } + + public string Name { + get { return _ast.Name; } + } + + public string Description { + get { return _ast.Documentation; } + } + + public SourceLocation Start { + get { return _ast.Start; } + } + + public SourceLocation End { + get { return _ast.End; } + } + + public IEnumerable NestedScopes { + get { + return EnumerateBody(_ast.Body); + } + } + + internal static IEnumerable EnumerateBody(Statement body) { + SuiteStatement suite = body as SuiteStatement; + if (suite != null) { + foreach (Statement stmt in suite.Statements) { + ClassDefinition klass = stmt as ClassDefinition; + if (klass != null) { + yield return new ClassScopeNode(klass); + } + + FunctionDefinition func = stmt as FunctionDefinition; + if (func != null) { + yield return new FunctionScopeNode(func); + } + } + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Navigation/ClassScopeNode.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Navigation/ClassScopeNode.cs new file mode 100644 index 0000000000..116c00b1a6 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Navigation/ClassScopeNode.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using Microsoft.IronStudio.Navigation; +using Microsoft.Scripting; + +namespace Microsoft.IronPythonTools.Navigation { + class ClassScopeNode : IScopeNode { + private readonly ClassDefinition _klass; + + public ClassScopeNode(ClassDefinition klass) { + _klass = klass; + } + + #region IScopeNode Members + + public bool IsFunction { + get { return false; } + } + + public string Name { + get { return _klass.Name; } + } + + public string Description { + get { return _klass.Body.Documentation; } + } + + public SourceLocation Start { + get { return _klass.Start; } + } + + public SourceLocation End { + get { return _klass.End; } + } + + public IEnumerable NestedScopes { + get { + return AstScopeNode.EnumerateBody(_klass.Body); + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Navigation/FunctionScopeNode.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Navigation/FunctionScopeNode.cs new file mode 100644 index 0000000000..c1566657da --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Navigation/FunctionScopeNode.cs @@ -0,0 +1,62 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using Microsoft.IronStudio.Navigation; +using Microsoft.Scripting; + +namespace Microsoft.IronPythonTools.Navigation { + class FunctionScopeNode : IScopeNode { + private readonly FunctionDefinition _func; + + public FunctionScopeNode(FunctionDefinition func) { + _func = func; + } + + public FunctionDefinition Definition { + get { + return _func; + } + } + + #region IScopeNode Members + + public bool IsFunction { + get { return true; } + } + + public string Name { + get { return _func.Name; } + } + + public string Description { + get { return _func.Body.Documentation; } + } + + public SourceLocation Start { + get { return _func.Start; } + } + + public SourceLocation End { + get { return _func.End; } + } + + public IEnumerable NestedScopes { + get { return AstScopeNode.EnumerateBody(_func.Body); } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/OutliningTaggerProvider.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/OutliningTaggerProvider.cs new file mode 100644 index 0000000000..52f288f543 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/OutliningTaggerProvider.cs @@ -0,0 +1,351 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Diagnostics; +using IronPython.Compiler.Ast; +using Microsoft.IronPythonTools.Intellisense; +using Microsoft.IronPythonTools.Internal; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Core; +using Microsoft.PyAnalysis; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities; +using System.Threading; + +namespace Microsoft.IronPythonTools { + [Export(typeof(ITaggerProvider)), ContentType(PythonCoreConstants.ContentType)] + [TagType(typeof(IOutliningRegionTag))] + class OutliningTaggerProvider : ITaggerProvider { + private readonly IPythonRuntimeHost _host; + + [ImportingConstructor] + public OutliningTaggerProvider(IPythonRuntimeHost host) { + _host = host; + } + + #region ITaggerProvider Members + + public ITagger CreateTagger(ITextBuffer buffer) where T : ITag { + return (ITagger)(buffer.GetOutliningTagger() ?? new OutliningTagger(buffer, _host)); + } + + #endregion + + internal class OutliningTagger : ITagger { + private readonly ITextBuffer _buffer; + private readonly Timer _timer; + private bool _enabled, _eventHooked; + + public OutliningTagger(ITextBuffer buffer, IPythonRuntimeHost host) { + _buffer = buffer; + _buffer.Properties[typeof(OutliningTagger)] = this; + _enabled = host.EnterOutliningModeOnOpen; + _timer = new Timer(TagUpdate, null, Timeout.Infinite, Timeout.Infinite); + } + + public bool Enabled { + get { + return _enabled; + } + } + + public void Enable() { + _enabled = true; + var snapshot = _buffer.CurrentSnapshot; + var tagsChanged = TagsChanged; + if (tagsChanged != null) { + tagsChanged(this, new SnapshotSpanEventArgs(new SnapshotSpan(snapshot, new Span(0, snapshot.Length)))); + } + } + + public void Disable() { + _enabled = false; + var snapshot = _buffer.CurrentSnapshot; + var tagsChanged = TagsChanged; + if (tagsChanged != null) { + tagsChanged(this, new SnapshotSpanEventArgs(new SnapshotSpan(snapshot, new Span(0, snapshot.Length)))); + } + } + + #region ITagger Members + + public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) { + IPythonProjectEntry classifier; + if (_enabled && _buffer.TryGetPythonAnalysis(out classifier)) { + if (!_eventHooked) { + classifier.OnNewParseTree += OnNewParseTree; + _eventHooked = true; + } + PythonAst ast; + IAnalysisCookie cookie; + classifier.GetTreeAndCookie(out ast, out cookie); + SnapshotCookie snapCookie = cookie as SnapshotCookie; + + if (ast != null && snapCookie != null) { + return ProcessSuite(spans, ast.Body as SuiteStatement, snapCookie.Snapshot, true); + } + } + + return new ITagSpan[0]; + } + + private void OnNewParseTree(object sender, EventArgs e) { + IPythonProjectEntry classifier; + if (_buffer.TryGetPythonAnalysis(out classifier)) { + _timer.Change(300, Timeout.Infinite); + } + } + + private void TagUpdate(object unused) { + _timer.Change(Timeout.Infinite, Timeout.Infinite); + var snapshot = _buffer.CurrentSnapshot; + var tagsChanged = TagsChanged; + if (tagsChanged != null) { + tagsChanged(this, new SnapshotSpanEventArgs(new SnapshotSpan(snapshot, new Span(0, snapshot.Length)))); + } + } + + private IEnumerable> ProcessSuite(NormalizedSnapshotSpanCollection spans, SuiteStatement suite, ITextSnapshot snapshot, bool isTopLevel) { + if (suite != null) { + // TODO: Binary search the statements? The perf of this seems fine for the time being + // w/ a 5000+ line file though. + foreach (var statement in suite.Statements) { + FunctionDefinition funcDef = statement as FunctionDefinition; + if (funcDef != null) { + SnapshotSpan? span = ShouldInclude(statement, spans); + if (span == null) { + continue; + } + + TagSpan tagSpan = null; + try { + int nameLen = funcDef.Header.Index - funcDef.Start.Index + 1; + if (funcDef.Start.IsValid && funcDef.End.IsValid) { + var funcSpan = GetFinalSpan(snapshot, + funcDef.Start.Index + nameLen, + funcDef.End.Index - funcDef.Start.Index - nameLen); + + tagSpan = new TagSpan( + new SnapshotSpan(snapshot, funcSpan), + new OutliningTag(snapshot, funcSpan, true) + ); + } + } catch (ArgumentException) { + // sometimes IronPython's parser gives usbad spans, ignore those and fix IronPython + Debug.Assert(false, "bad argument when making span/tag"); + } + + if (tagSpan != null) { + yield return tagSpan; + } + } + + ClassDefinition classDef = statement as ClassDefinition; + if (classDef != null) { + SnapshotSpan? span = ShouldInclude(statement, spans); + if (span != null) { + TagSpan tagSpan = null; + try { + int nameLen = classDef.Header.Index - classDef.Start.Index + 1; + if (classDef.Start.IsValid && classDef.End.IsValid) { + var classSpan = GetFinalSpan(snapshot, + classDef.Start.Index + nameLen, + classDef.End.Index - classDef.Start.Index - nameLen - 2 + ); + + tagSpan = new TagSpan( + new SnapshotSpan(snapshot, classSpan), + new OutliningTag(snapshot, classSpan, false) + ); + } + } catch (ArgumentException) { + // sometimes IronPython's parser gives usbad spans, ignore those and fix IronPython + Debug.Assert(false, "bad argument when making span/tag"); + } + + if (tagSpan != null) { + yield return tagSpan; + } + } + + // recurse into the class definition and outline it's members + foreach (var v in ProcessSuite(spans, classDef.Body as SuiteStatement, snapshot, false)) { + yield return v; + } + } + + if (isTopLevel) { + IfStatement ifStmt = statement as IfStatement; + if (ifStmt != null) { + SnapshotSpan? span = ShouldInclude(statement, spans); + if (span != null) { + TagSpan tagSpan = null; + try { + var testLen = ifStmt.Tests[0].Header.Index - ifStmt.Start.Index + 1; + if (ifStmt.Start.IsValid && ifStmt.End.IsValid) { + var ifSpan = GetFinalSpan(snapshot, + ifStmt.Start.Index + testLen, + ifStmt.End.Index - ifStmt.Start.Index - testLen - 2 + ); + + tagSpan = new TagSpan( + new SnapshotSpan(snapshot, ifSpan), + new OutliningTag(snapshot, ifSpan, false) + ); + } + } catch (ArgumentException) { + // sometimes IronPython's parser gives usbad spans, ignore those and fix IronPython + Debug.Assert(false, "bad argument when making span/tag"); + } + + if (tagSpan != null) { + yield return tagSpan; + } + } + } + } + } + } + } + + private static Span GetFinalSpan(ITextSnapshot snapshot, int start, int length) { + int cnt = 0; + var text = snapshot.GetText(start, length); + + // remove up to 2 \r\n's if we just end with these, this will leave a space between the methods + while (length > 0 && ((Char.IsWhiteSpace(text[length - 1])) || ((text[length - 1] == '\r' || text[length - 1] == '\n') && cnt++ < 4))) { + length--; + } + return new Span(start, length); + } + + private SnapshotSpan? ShouldInclude(Statement statement, NormalizedSnapshotSpanCollection spans) { + if (spans.Count == 1 && spans[0].Length == spans[0].Snapshot.Length) { + // we're processing the entire snapshot + return spans[0]; + } + + for (int i = 0; i < spans.Count; i++) { + if (spans[i].IntersectsWith(new Span(statement.Start.Index, statement.End.Index))) { + return spans[i]; + } + } + return null; + } + + public event EventHandler TagsChanged; + + #endregion + } + + class TagSpan : ITagSpan { + private readonly SnapshotSpan _span; + private readonly OutliningTag _tag; + + public TagSpan(SnapshotSpan span, OutliningTag tag) { + _span = span; + _tag = tag; + } + + #region ITagSpan Members + + public SnapshotSpan Span { + get { return _span; } + } + + public IOutliningRegionTag Tag { + get { return _tag; } + } + + #endregion + } + + class OutliningTag : IOutliningRegionTag { + private readonly ITextSnapshot _snapshot; + private readonly Span _span; + private readonly bool _isImplementation; + + public OutliningTag(ITextSnapshot iTextSnapshot, Span span, bool isImplementation) { + _snapshot = iTextSnapshot; + _span = span; + _isImplementation = isImplementation; + } + + #region IOutliningRegionTag Members + + public object CollapsedForm { + get { return "..."; } + } + + public object CollapsedHintForm { + get { + string collapsedHint = _snapshot.GetText(_span); + + string[] lines = collapsedHint.Split(new string[] { "\r\n" }, StringSplitOptions.None); + // remove any leading white space for the preview + if (lines.Length > 0) { + int smallestWhiteSpace = Int32.MaxValue; + for (int i = 0; i < lines.Length; i++) { + string curLine = lines[i]; + + for (int j = 0; j < curLine.Length; j++) { + if (curLine[j] != ' ') { + smallestWhiteSpace = Math.Min(j, smallestWhiteSpace); + } + } + } + + for (int i = 0; i < lines.Length; i++) { + if (lines[i].Length >= smallestWhiteSpace) { + lines[i] = lines[i].Substring(smallestWhiteSpace); + } + } + + return String.Join("\r\n", lines); + } + return collapsedHint; + } + } + + public bool IsDefaultCollapsed { + get { return false; } + } + + public bool IsImplementation { + get { return _isImplementation; } + } + + #endregion + } + } + + static class OutliningTaggerProviderExtensions { + public static OutliningTaggerProvider.OutliningTagger GetOutliningTagger(this ITextView self) { + return self.TextBuffer.GetOutliningTagger(); + } + + public static OutliningTaggerProvider.OutliningTagger GetOutliningTagger(this ITextBuffer self) { + OutliningTaggerProvider.OutliningTagger res; + if (self.Properties.TryGetProperty(typeof(OutliningTaggerProvider.OutliningTagger), out res)) { + return res; + } + return null; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/PythonClassifierProvider.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/PythonClassifierProvider.cs new file mode 100644 index 0000000000..b35cc856d1 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/PythonClassifierProvider.cs @@ -0,0 +1,61 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel.Composition; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Core; +using Microsoft.IronStudio.Library; +using Microsoft.Scripting.Hosting; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronPythonTools { + /// + /// Python classifier provider - we just subclass the DLR classifier provider and + /// give it our engine and content type. + /// + [Export(typeof(IClassifierProvider)), ContentType(PythonCoreConstants.ContentType)] + public class PythonClassifierProvider : DlrClassifierProvider { + private readonly IContentType _type; + private readonly ScriptEngine _engine; + public static PythonClassifierProvider Instance; + + [ImportingConstructor] + public PythonClassifierProvider(IPythonRuntimeHost host) + : this(host.ContentType, host.ScriptEngine) { + Instance = this; + } + + public PythonClassifierProvider(IContentType contentType, ScriptEngine scriptEngine) { + _type = contentType; + _engine = scriptEngine; + } + + #region IDlrClassifierProvider + + public override IContentType ContentType { + get { + return _type; + } + } + + public override ScriptEngine Engine { + get { + return _engine; + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/PythonCoreConstants.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/PythonCoreConstants.cs new file mode 100644 index 0000000000..4e7299b393 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/PythonCoreConstants.cs @@ -0,0 +1,26 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel.Composition; +using Microsoft.IronStudio; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronPythonTools { + internal static class PythonCoreConstants { + public const string ContentType = "IronPython"; + + [Export, Name(ContentType), BaseDefinition(CoreConstants.DlrContentTypeName)] + internal static ContentTypeDefinition ContentTypeDefinition = null; + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/PythonRuntimeHost.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/PythonRuntimeHost.cs new file mode 100644 index 0000000000..c6ab4b243e --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/PythonRuntimeHost.cs @@ -0,0 +1,127 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.IO; +using IronPython.Hosting; +using Microsoft.IronStudio.Core; +using Microsoft.Scripting.Hosting; +using Microsoft.VisualStudio.Utilities; +using Microsoft.Win32; + +namespace Microsoft.IronPythonTools { + [Export(typeof(IPythonRuntimeHost))] + public sealed class PythonRuntimeHost : IPythonRuntimeHost { + private readonly IContentType _contentType; + private readonly ScriptEngine _engine; + private bool _enterOutliningOnOpen, _intersectMembers, _hideAdvancedMembers; + + [ImportingConstructor] + internal PythonRuntimeHost(IContentTypeRegistryService/*!*/ contentTypeRegistryService, IFileExtensionRegistryService/*!*/ fileExtensionRegistryService) { + _engine = Python.CreateEngine(new Dictionary { { "NoAssemblyResolveHook", true } }); + _contentType = contentTypeRegistryService.GetContentType(PythonCoreConstants.ContentType); + CoreUtils.RegisterExtensions(contentTypeRegistryService, fileExtensionRegistryService, _contentType, _engine.Setup.FileExtensions); + } + + public ScriptEngine ScriptEngine { + get { + return _engine; + } + } + + public IContentType ContentType { + get { + return _contentType; + } + } + + public bool EnterOutliningModeOnOpen { + get { + return _enterOutliningOnOpen; + } + set { + _enterOutliningOnOpen = value; + } + } + + public bool IntersectMembers { + get { + return _intersectMembers; + } + set { + _intersectMembers = value; + } + } + + public bool HideAdvancedMembers { + get { + return _hideAdvancedMembers; + } + set { + _hideAdvancedMembers = value; + } + } + + internal static string GetPythonInstallDir() { +#if DEBUG + string result = Environment.GetEnvironmentVariable("DLR_ROOT"); + if (result != null) { + result = Path.Combine(result, @"Bin\Debug"); + if (PythonRuntimeHost.IronPythonExistsIn(result)) { + return result; + } + } +#endif + + using (var ipy = Registry.LocalMachine.OpenSubKey("SOFTWARE\\Microsoft\\IronPython")) { + if (ipy != null) { + using (var twoSeven = ipy.OpenSubKey("2.7")) { + if (twoSeven != null) { + var path = twoSeven.GetValue("") as string; + if (path != null) { + return path; + } + } + } + } + } + + var paths = Environment.GetEnvironmentVariable("PATH"); + if (paths != null) { + foreach (string dir in paths.Split(Path.PathSeparator)) { + try { + if (IronPythonExistsIn(dir)) { + return dir; + } + } catch { + // ignore + } + } + } + + string extensionDir = Path.GetDirectoryName(typeof(PythonRuntimeHost).Assembly.GetFiles()[0].Name); + if (PythonRuntimeHost.IronPythonExistsIn(extensionDir)) { + return extensionDir; + } + + return null; + } + + private static bool IronPythonExistsIn(string/*!*/ dir) { + return File.Exists(Path.Combine(dir, "ipy.exe")); + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Repl/PythonEvaluator.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Repl/PythonEvaluator.cs new file mode 100644 index 0000000000..2ca5aab305 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Repl/PythonEvaluator.cs @@ -0,0 +1,298 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using IronPython; +using IronPython.Compiler; +using IronPython.Compiler.Ast; +using IronPython.Hosting; +using IronPython.Runtime.Types; +using Microsoft.IronStudio.Library.Repl; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Hosting.Providers; +using Microsoft.Scripting.Library; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.IronPythonTools.Library.Repl { + public class PythonEvaluator : DlrEvaluator { + private static ScriptEngine _localEngine = Python.CreateEngine(); // local engine for checking syntax + + // Constructed via reflection when deserialized from the registry. + public PythonEvaluator() + : base("python") { + } + + public override void Start() { + base.Start(); + + InitScope(MakeScope("__main__")); + InitThread(); + } + + public virtual void PublishScopeVariables(ScriptScope scope) { + } + + protected override void InitScope(ScriptScope scope) { + base.InitScope(scope); + PublishScopeVariables(scope); + } + + public override void Reset() { + } + + protected override ScriptEngine MakeEngine(Stream stream, TextWriter writer, TextReader reader) { +#if true + // Debuggable mode. + Dictionary opts = new Dictionary(); + + opts["Debug"] = true; // enable to emit PDBs for py code + opts["Frames"] = opts["FullFrames"] = true; // Set these to gen $localContext.Scope + + var result = Python.CreateEngine(opts); +#else + // Non-debuggable mode + var result = Python.CreateEngine(); +#endif + InitializeEngine(stream, writer, result); + return result; + } + + protected static void InitializeEngine(Stream stream, TextWriter writer, ScriptEngine result) { + result.Runtime.IO.SetOutput(stream, writer); + result.Runtime.LoadAssembly(typeof(string).Assembly); // mscorlib.dll + result.Runtime.LoadAssembly(typeof(System.Uri).Assembly); // System.dll + } + + public override bool CanExecuteText(string/*!*/ text) { + // Multi-line: when you leave two blank lines in a row, your thought must be done + // TODO: This feels unsatisfactory, even if it largely matches Python behavior under cmd.exe + int newLines = 0; + for (int i = text.Length - 1; i >= 0; i--) { + if (text[i] == '\n') { + if (++newLines == 2) { + return true; + } + } else if (Char.IsWhiteSpace(text[i])) { + continue; + } else { + break; + } + } + + // If this a partially-formed thought (for instance, open brace or block), don't execute + if (!base.CanExecuteText(text)) { + return false; + } + + // Single-line: if it's executable, then execute + if (text.IndexOf('\n') == text.LastIndexOf('\n')) { + return true; + } + + return false; + } + + protected override bool ShouldEvaluateForCompletion(string source) { + var scriptSrc = _localEngine.CreateScriptSource(new StringTextContentProvider(source), "", SourceCodeKind.Expression); + var context = new CompilerContext(HostingHelpers.GetSourceUnit(scriptSrc), HostingHelpers.GetLanguageContext(_localEngine).GetCompilerOptions(), ErrorSink.Null); + var parser = Parser.CreateParser(context, new PythonOptions()); + + var stmt = parser.ParseSingleStatement(); + var exprWalker = new ExprWalker(); + stmt.Walk(exprWalker); + return exprWalker.ShouldExecute; + } + + class ExprWalker : PythonWalker { + public bool ShouldExecute = true; + + public override bool Walk(CallExpression node) { + ShouldExecute = false; + return base.Walk(node); + } + } + + protected virtual ScriptScope MakeScope(string name) { + return _engine.CreateModule(name); + } + + private Scope GetModule(string name) { + var sysmodule = _engine.GetSysModule(); + var modules = sysmodule.GetVariable>("modules"); + object result = null; + modules.TryGetValue(name, out result); + return result as Scope; + } + + //[ReplCommand("reload") + public void ReloadModule(string name) { + if (GetModule(name) == null) { + throw new Exception(String.Format("Module '{0}' not found", name)); + } + +#if CAN_USE_PYTHON + from project_analyzer import GetAnalysisEngine + engine = GetAnalysisEngine() + if engine is None: + raise RuntimeError("Analysis engine is not enabled") + + if self._project is None: + raise RuntimeError("Not attached to a project") + + analysis = engine.GetProjectAnalysisState(self._project) + if analysis is None: + msg = "Unable to get analysis for project %r" % (self._project,) + raise RuntimeError(msg) + + import dependency_import + d = dependency_import.get_dependencies(analysis, name) + m = [] + + def test_loaded(n): + if self._GetModule(n) is None: + return False + m.append(n) + return True + + d.WalkTree(test_loaded) + + // TODO: Run on engine thread + b = IronPython.Hosting.Python.GetBuiltinModule(self._engine) + for m in [self._GetModule(n) for n in m]: + self.WriteLine('Reloading ' + m.__name__) + self._engine.Operations.InvokeMember(b, 'reload', m) +#endif + } + + protected override SourceCodeKind SourceCodeKind { + get { + return SourceCodeKind.InteractiveCode; + } + } + + protected override ScriptScope ScopeForLastResult { + get { + return _engine.GetBuiltinModule(); + } + } + + protected override string[] FilterNames(IList names, string startsWith) { + string[] n = base.FilterNames(names, startsWith); + for (int i = 0; i < n.Length; i++) { + if (n[i].StartsWith("__") && n[i].EndsWith("__")) { + continue; + } + + string[] n2 = new string[n.Length]; + int pivot = n.Length - i; + for (int j = 0; j < pivot; j++) { + n2[j] = n[i + j]; + } + for (int j = 0; j < i; j++) { + n2[pivot + j] = n[j]; + } + n = n2; + break; + } + return n; + } +#if FALSE + public override Member[] GetModules() { + var sysmodule = _engine.GetSysModule(); + var path = sysmodule.GetVariable("path"); + var mods = new List(); + foreach (string dir in path) { + if (!Directory.Exists(dir)) { + continue; + } + + foreach (string filename in Directory.GetFiles(dir)) { + mods.Add(new Member { Name = Path.GetFileNameWithoutExtension(filename) }); + } + foreach (string dirname in Directory.GetDirectories(dir)) { + if (File.Exists(Path.Combine(dirname, "__init__.py"))) { + mods.Add(new Member { Name = Path.GetFileName(dir) }); + } + } + } + + return mods.ToArray(); + } +#endif + private static bool IsObjectNew(object obj) { + BuiltinFunction newFn = (obj as BuiltinFunction); + if (newFn == null) { + return false; + } + return (newFn.DeclaringType == typeof(object) && newFn.__name__ == "__new__"); + } +#if FALSE + public override ReplOverloadResult[] GetSignatures(string text) { + var obj = GetRootObject(); + if (obj == null) { + return new ReplOverloadResult[0]; + } + + var t = text.Remove(text.Length - 1); + foreach (var symbol in t.Split('.')) { + obj = GetObjectMember(obj, symbol); + if (obj == null) { + return new ReplOverloadResult[0]; + } + } + + var sigs = new List(); + + if (obj is BuiltinFunction) { + // TODO: Deal with "clr" visibility + BuiltinFunction bf = (obj as BuiltinFunction); + foreach (object overload in bf.Overloads.Functions) { + sigs.Add(new BuiltinFunctionOverloadResult(this, overload)); + } + } else if (obj is PythonFunction || obj is Method) { + sigs.Add(new FunctionOverloadResult(this, obj)); + } else if (obj is PythonType || obj is OldClass) { + var name = GetObjectMember(obj, "__name__") as string; + var docstring = GetObjectMember(obj, "__doc__") as string; + var newFunc = GetObjectMember(obj, "__new__"); + if (!IsObjectNew(newFunc)) { + if (newFunc is PythonFunction || newFunc is Method) { + sigs.Add(new FunctionOverloadResult(this, newFunc, name, docstring, true)); + } else { + // TODO: Deal with "clr" visibility + BuiltinFunction bf = (obj as BuiltinFunction); + foreach (object overload in bf.Overloads.Functions) { + sigs.Add(new BuiltinFunctionOverloadResult(this, overload, name)); + } + } + } else { + var init = GetObjectMember(obj, "__init__"); + var constructor = GetObjectMember(init, "im_func"); + sigs.Add(new FunctionOverloadResult(this, constructor, name, docstring, true)); + } + } + return sigs.ToArray(); + } +#endif + + public override bool EnableMultipleScopes { + get { + return false; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Repl/RemotePythonEvaluator.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Repl/RemotePythonEvaluator.cs new file mode 100644 index 0000000000..24624a0fa2 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Repl/RemotePythonEvaluator.cs @@ -0,0 +1,215 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.Remoting; +using System.Threading; +using IronPython.Hosting; +using Microsoft.IronStudio.RemoteEvaluation; +using Microsoft.IronStudio.Repl; +using Microsoft.Scripting.Hosting; + +namespace Microsoft.IronPythonTools.Library.Repl { + + public class RemotePythonEvaluator : PythonEvaluator, IMultipleScopeEvaluator { + private RemoteScriptFactory _factory; + private string _currentScopeName; + + static RemotePythonEvaluator() { + AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); + } + + // Constructed via reflection when deserialized from the registry. + public RemotePythonEvaluator() { + _factory = CreateFactory(); + + } + + public static RemoteScriptFactory CreateFactory() { + return new RemoteScriptFactory(ApartmentState.STA); + } + + public RemoteScriptFactory RemoteScriptFactory { + get { + return _factory; + } + } + + public override void PublishScopeVariables(ScriptScope scope) { + } + + public override void Start() { + _currentScopeName = "__main__"; + base.Start(); + } + + public override void Restart() { + WriteLine("Remote process has exited, restarting..."); + _factory = CreateFactory(); + Start(); + _factory.CommandDispatcher = _engine.GetService(_engine).GetLocalCommandDispatcher(); + + var changed = AvailableScopesChanged; + if (changed != null) { + changed(this, EventArgs.Empty); + } + } + + public override void Reset() { + WriteLine("Remote process has been reset..."); + _factory.Shutdown(); + + _factory = CreateFactory(); + Start(); + _factory.CommandDispatcher = _engine.GetService(_engine).GetLocalCommandDispatcher(); + + var changed = AvailableScopesChanged; + if (changed != null) { + changed(this, EventArgs.Empty); + } + } + + public override bool ExecuteText(string text, Action completionFunction) { + if (_factory.IsDisconnected) { + Restart(); + } + + return base.ExecuteText(text, completionFunction); + } + + public override string FormatException(ObjectHandle exception) { + if (_factory.IsDisconnected) { + Restart(); + } + + return base.FormatException(exception); + } + + public override bool CanExecuteText(string/*!*/ text) { + if (_factory.IsDisconnected) { + Restart(); + } + + return base.CanExecuteText(text); + } + + static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { + // VS Loads us into the LoadFrom context, remoting needs to get the same assemblies + // but uses Assembly.Load. We return the correct assemblies here. + if (args.Name == typeof(ScriptRuntime).Assembly.FullName) { + return typeof(ScriptRuntime).Assembly; + } else if (args.Name == typeof(Python).Assembly.FullName) { + return typeof(Python).Assembly; + } + return null; + } + + public virtual Dictionary GetOptions() { + return new Dictionary(); + } + + protected override ScriptEngine MakeEngine(Stream stream, TextWriter writer, TextReader reader) { + _factory.SetConsoleOut(writer); + _factory.SetConsoleError(writer); + _factory.SetConsoleIn(reader); + + var runtime = (ScriptRuntime)_factory.CreateRuntime(Python.CreateRuntimeSetup(GetOptions())); + var res = runtime.GetEngine("Python"); + InitializeEngine(stream, writer, res); + return res; + } + + public override void AbortCommand() { + ThreadPool.QueueUserWorkItem(x => _factory.Abort()); + } + + + public event EventHandler AvailableScopesChanged; + public event EventHandler CurrentScopeChanged; + + public IEnumerable GetAvailableScopes() { + return _engine.GetModuleFilenames(); + } + + public override ICollection GetSignatureDocumentation(string expression) { + if (_factory.IsDisconnected) { + Restart(); + } + + return base.GetSignatureDocumentation(expression); + } + + public override ICollection GetMemberNames(string expression) { + if (_factory.IsDisconnected) { + Restart(); + } + + return base.GetMemberNames(expression); + } + + public void SetScope(string scopeName) { + try { + _currentScope = _engine.ImportModule(scopeName); + _currentScopeName = scopeName; + WriteLine(String.Format("Current scope changed to {0}", scopeName)); + + var curScopeChanged = CurrentScopeChanged; + if (curScopeChanged != null) { + curScopeChanged(this, EventArgs.Empty); + } + } catch { + WriteLine(String.Format("Unknown module: {0}", scopeName)); + } + } + + public string SetScope(ScriptScope scope) { + _currentScope = scope; + string scopeName; + + if (scope.TryGetVariable("__name__", out scopeName)) { + _currentScopeName = scopeName; + } else { + _currentScopeName = String.Empty; + } + + var curScopeChanged = CurrentScopeChanged; + if (curScopeChanged != null) { + curScopeChanged(this, EventArgs.Empty); + } + + return _currentScopeName; + } + + public override bool EnableMultipleScopes { + get { + return true; + } + } + + public string CurrentScopeName { + get { + return _currentScopeName; + } + } + + public ScriptScope CurrentScope { + get { + return _currentScope; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Repl/SwitchModuleCommand.cs b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Repl/SwitchModuleCommand.cs new file mode 100644 index 0000000000..6e237a247d --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/IronPythonToolsCore/Repl/SwitchModuleCommand.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel.Composition; + +using Microsoft.IronStudio.Repl; +using Microsoft.IronPythonTools.Library.Repl; + +namespace Microsoft.IronPythonTools.Repl { + [Export(typeof(IReplCommand))] + class SwitchModuleCommand : IReplCommand { + #region IReplCommand Members + + public void Execute(IReplWindow window, string arguments) { + var remoteEval = window.Evaluator as RemotePythonEvaluator; + if(remoteEval != null) { + remoteEval.SetScope(arguments); + } + } + + public string Description { + get { return "Switches the current scope to the specified module name."; } + } + + public string Command { + get { return "mod"; } + } + + public object ButtonContent { + get { + return null; + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/Key.snk b/Tools/IronStudio/IronPythonToolsCore/Key.snk new file mode 100644 index 0000000000..5a0c071e00 Binary files /dev/null and b/Tools/IronStudio/IronPythonToolsCore/Key.snk differ diff --git a/Tools/IronStudio/IronPythonToolsCore/Properties/AssemblyInfo.cs b/Tools/IronStudio/IronPythonToolsCore/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..cf946a8eb6 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/Properties/AssemblyInfo.cs @@ -0,0 +1,46 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("IronPythonToolsCore")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("IronPythonToolsCore")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("e616f995-4e0b-4948-834d-ba44bdddb3e4")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +#if MS_SIGNED +[assembly: InternalsVisibleTo("IronPythonTools, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("AnalysisTest, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +#else +[assembly: InternalsVisibleTo("IronPythonTools, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c10ce00dd2e0ce5046d68183d3ad035b47e92bf0ce7bcf8a03a217ca5d0b0c7db973fdf97579b52b502a23d4069dbf043389e1ab65a1d6c508a9837f3e2350f15e05cc63c0fc4b0410867a51919090e4c33f80203e9b0035b21c32bae20f98b068f90d99a50133a5336480d94039b176519f5fd8524765f33be43da65c4b68ba")] +[assembly: InternalsVisibleTo("AnalysisTest, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c10ce00dd2e0ce5046d68183d3ad035b47e92bf0ce7bcf8a03a217ca5d0b0c7db973fdf97579b52b502a23d4069dbf043389e1ab65a1d6c508a9837f3e2350f15e05cc63c0fc4b0410867a51919090e4c33f80203e9b0035b21c32bae20f98b068f90d99a50133a5336480d94039b176519f5fd8524765f33be43da65c4b68ba")] +[assembly: InternalsVisibleTo("UnitTests, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c10ce00dd2e0ce5046d68183d3ad035b47e92bf0ce7bcf8a03a217ca5d0b0c7db973fdf97579b52b502a23d4069dbf043389e1ab65a1d6c508a9837f3e2350f15e05cc63c0fc4b0410867a51919090e4c33f80203e9b0035b21c32bae20f98b068f90d99a50133a5336480d94039b176519f5fd8524765f33be43da65c4b68ba")] +#endif + diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/HashSetExtensions.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/HashSetExtensions.cs new file mode 100644 index 0000000000..2be09f011b --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/HashSetExtensions.cs @@ -0,0 +1,62 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.PyAnalysis.Values; + +namespace Microsoft.PyAnalysis { + internal static class HashSetExtensions { + public static ISet Union(this ISet self, ISet value) { + bool dummy = false; + return Union(self, value, ref dummy); + } + + /// + /// Returns the union of the new two sets as a new set tracking whether + /// or not self is a locally created HashSet. + /// + public static ISet Union(this ISet self, ISet value, ref bool madeSet) { + Namespace selfOne, valueOne; + + if (self.Count == 0) { + return value; + } else if (value.Count == 0) { + return self; + } else if ((selfOne = self as Namespace) != null) { + if ((valueOne = value as Namespace) != null) { + var res = new HashSet(); + res.Add(selfOne); + res.Add(valueOne); + return res; + } else { + var res = new HashSet(value); + res.Add(selfOne); + return res; + } + } else if ((valueOne = value as Namespace) != null) { + var res = new HashSet(self); + res.Add(valueOne); + return res; + } + + if (!madeSet) { + self = new HashSet(self); + madeSet = true; + } + self.UnionWith(value); + return self; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/IAnalysisCookie.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/IAnalysisCookie.cs new file mode 100644 index 0000000000..1c867a3c87 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/IAnalysisCookie.cs @@ -0,0 +1,28 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.PyAnalysis { + /// + /// Used to track information about where the analysis came from and + /// get back the original content. + /// + public interface IAnalysisCookie { + string GetLine(int lineNo); + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/IAnalysisValue.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/IAnalysisValue.cs new file mode 100644 index 0000000000..366a669f00 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/IAnalysisValue.cs @@ -0,0 +1,63 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.PyAnalysis.Values; +using IronPython.Runtime.Types; + +namespace Microsoft.PyAnalysis { + /// + /// Represents the result from an analysis lookup. + /// + public interface IAnalysisValue { + /// + /// Gets a description of the variable result. + /// + string Description { + get; + } + + /// + /// Gets a short description of the variable result. + /// + string ShortDescription { + get; + } + + /// + /// Returns the location of where the variable is defined. + /// + LocationInfo Location { + get; + } + + IEnumerable References { + get; + } + + /// + /// Gets the type of variable result. + /// + ResultType ResultType { + get; + } + + /// + /// Gets the concrete type used by IronPython or null if it does not have a concrete type. + /// + PythonType PythonType { + get; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/IAnalysisVariable.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/IAnalysisVariable.cs new file mode 100644 index 0000000000..f3b339fdc1 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/IAnalysisVariable.cs @@ -0,0 +1,61 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.PyAnalysis { + public interface IAnalysisVariable { + /// + /// Returns the location of where the variable is defined. + /// + LocationInfo Location { + get; + } + + VariableType Type { + get; + } + } + + public enum VariableType { + None, + /// + /// A parameter to a function definition or assignment to a member or global. + /// + Definition, + + /// + /// A read from a global, local, member variable. + /// + Reference, + + /// + /// A reference to a value which is passed into a parameter. + /// + Value + } + + class AnalysisVariable : IAnalysisVariable { + private readonly LocationInfo _loc; + private readonly VariableType _type; + + public AnalysisVariable(VariableType type, LocationInfo location) { + _loc = location; + _type = type; + } + + #region IAnalysisVariable Members + + public LocationInfo Location { + get { return _loc; } + } + + public VariableType Type { + get { return _type; } + } + + #endregion + } + + +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/IOverloadResult.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/IOverloadResult.cs new file mode 100644 index 0000000000..6bc8f9b5c3 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/IOverloadResult.cs @@ -0,0 +1,53 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.PyAnalysis { + public class ParameterResult { + public string Name { get; private set; } + public string Documentation { get; private set; } + public string Type { get; private set; } + public bool IsOptional { get; private set; } + + public ParameterResult(string name) + : this(name, String.Empty, "object") { + } + public ParameterResult(string name, string doc) + : this(name, doc, "object") { + } + public ParameterResult(string name, string doc, string type) + : this(name, doc, type, false) { + } + public ParameterResult(string name, string doc, string type, bool isOptional) { + Name = name; + Documentation = Trim(doc); + Type = type; + IsOptional = isOptional; + } + + private const int MaxDocLength = 1000; + internal static string Trim(string doc) { + if (doc != null && doc.Length > MaxDocLength) { + return doc.Substring(0, MaxDocLength) + "..."; + } + return doc; + } + } + public interface IOverloadResult { + string Name { get; } + string Documentation { get; } + ParameterResult[] Parameters { get; } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ImportInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ImportInfo.cs new file mode 100644 index 0000000000..4bdf87f802 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ImportInfo.cs @@ -0,0 +1,37 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.Scripting; + +namespace Microsoft.PyAnalysis { + /// + /// Stores info about import statements. Namespace will be an empty string for simple "import wpf"s + /// + public class ImportInfo { + private readonly string _namespace; + private readonly SourceSpan _span; + private readonly List _types; + + public ImportInfo(string dottedName, SourceSpan span) { + _namespace = dottedName; + _span = span; + _types = new List(); + } + + public List Types { + get { return _types; } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/AnalysisUnit.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/AnalysisUnit.cs new file mode 100644 index 0000000000..0c8b40a329 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/AnalysisUnit.cs @@ -0,0 +1,145 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Values; + +namespace Microsoft.PyAnalysis.Interpreter { + /// + /// Encapsulates a single piece of code which can be analyzed. Currently this could be a top-level module, a class definition, or + /// a function definition. AnalysisUnit holds onto both the AST of the code which is to be analyzed along with + /// the scope in which the object is declared. + /// + internal class AnalysisUnit { + private readonly Node _ast; + private readonly InterpreterScope[] _scopes; + private readonly AnalysisUnit _parent; + private bool _inQueue, _forEval; + + public AnalysisUnit(Node node, InterpreterScope[] scopes, AnalysisUnit parent) { + _ast = node; + _scopes = scopes; + _parent = parent; + } + + private AnalysisUnit(Node ast, InterpreterScope[] scopes, AnalysisUnit parent, bool forEval) { + _ast = ast; + _scopes = scopes; + _parent = parent; + _forEval = forEval; + } + + public bool IsInQueue { + get { + var cur = this; + do { + if (_inQueue) { + return true; + } + + cur = cur._parent; + } while (cur != null); + + return false; + } + set { + _inQueue = value; + } + } + + /// + /// True if this analysis unit is being used to evaluate the result of the analysis. In this + /// mode we don't track references or re-queue items. + /// + public bool ForEval { + get { + return _forEval; + } + } + + public AnalysisUnit CopyForEval() { + return new AnalysisUnit(_ast, _scopes, _parent, true); + } + + public AnalysisUnit Parent { + get { + return _parent; + } + } + + public void Enqueue() { + if (!ForEval && !IsInQueue) { + Queue.Enqueue(this); + this.IsInQueue = true; + } + } + + /// + /// The queue this analysis unit is associated with + /// + public Queue Queue { + get { + return ProjectState.Queue; + } + } + + /// + /// The global scope that the code associated with this analysis unit is declared within. + /// + public ModuleInfo DeclaringModule { + get { + + Debug.Assert(_scopes[0] != null); + return ((ModuleScope)_scopes[0]).Module; + } + } + + public IProjectEntry ProjectEntry { + get { + return DeclaringModule.ProjectEntry; + } + } + + public ProjectState ProjectState { + get { + return DeclaringModule.ProjectEntry.ProjectState; + } + } + + /// The AST which will be analyzed when this node is analyzed + /// + public Node Ast { + get { return _ast; } + } + + /// + /// The chain of scopes in which this analysis is defined. + /// + public InterpreterScope[] Scopes { + get { return _scopes; } + } + + public override string ToString() { + return String.Format( + "<_AnalysisUnit: ModuleName={0}, NodeType={1}, ScopeName={2}>", + ((ModuleScope)_scopes[1]).Name, + _ast.GetType().Name, + _scopes[_scopes.Length - 1].Name + ); + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/ClassScope.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/ClassScope.cs new file mode 100644 index 0000000000..677acdcdff --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/ClassScope.cs @@ -0,0 +1,39 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using Microsoft.PyAnalysis.Values; + +namespace Microsoft.PyAnalysis.Interpreter { + class ClassScope : InterpreterScope { + public ClassScope(ClassInfo classInfo) + : base(classInfo) { + } + + public ClassInfo Class { + get { + return Namespace as ClassInfo; + } + } + + public override string Name { + get { return Class.ClassDefinition.Name; } + } + + public override bool VisibleToChildren { + get { + return false; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/DDG.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/DDG.cs new file mode 100644 index 0000000000..b3d2887c1b --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/DDG.cs @@ -0,0 +1,670 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Values; + +namespace Microsoft.PyAnalysis.Interpreter { + internal class DDG : PythonWalker { + private AnalysisUnit _unit; + private ExpressionEvaluator _eval; + + public void Analyze(Queuequeue) { + while (queue.Count > 0) { + _unit = queue.Dequeue(); + _unit.IsInQueue = false; + + _eval = new ExpressionEvaluator(_unit); + _unit.Ast.Walk(this); + } + } + + public InterpreterScope[] Scopes { + get { return _unit.Scopes; } + } + + public ModuleInfo GlobalScope { + get { return _unit.DeclaringModule; } + } + + public ProjectState ProjectState { + get { return _unit.ProjectState; } + } + + public override bool Walk(PythonAst node) { + ModuleReference existingRef; + Debug.Assert(node == _unit.Ast); + + if (ProjectState.Modules.TryGetValue(_unit.DeclaringModule.Name, out existingRef)) { + // if we have dependencies from files which were processed before us + // we need to re-enqueue those files. + if (existingRef.References != null) { + foreach (var referer in existingRef.References) { + referer.Enqueue(); + } + + // we won't need to process these again, we'll track all of our future dependencies + // via VariableDef's. + existingRef.References = null; + } + } else { + // publish our module ref now so that we don't collect dependencies as we'll be fully processed + ProjectState.Modules[_unit.DeclaringModule.Name] = new ModuleReference(_unit.DeclaringModule); + } + + return base.Walk(node); + } + + /// + /// Gets the function which we are processing code for currently or + /// null if we are not inside of a function body. + /// + public FunctionScope CurrentFunction { + get { return CurrentContainer(); } + } + + public ClassScope CurrentClass { + get { return CurrentContainer(); } + } + + private T CurrentContainer() where T : InterpreterScope { + for (int i = Scopes.Length - 1; i >= 0; i--) { + T result = Scopes[i] as T; + if (result != null) { + return result; + } + } + return null; + } + + public T LookupDefinition(Node node, string name) where T : Namespace { + var defined = _eval.LookupNamespaceByName(node, name, false); + foreach (var definition in defined) { + T result = definition as T; + if (result != null) { + return result; + } + } + return null; + } + + private void AssignTo(Node assignStmt, Expression left, ISet values) { + if (left is NameExpression) { + var l = (NameExpression)left; + var vars = _eval.LookupVariableByName(l.Name, l, false); + if (vars == null) { + vars = Scopes[Scopes.Length - 1].CreateVariable(left, _unit, l.Name, false); + } + + vars.AddAssignment(left, _unit); + vars.AddTypes(l, _unit, values); + } else if (left is MemberExpression) { + var l = (MemberExpression)left; + foreach (var obj in _eval.Evaluate(l.Target)) { + obj.SetMember(assignStmt, _unit, l.Name, values); + } + } else if (left is IndexExpression) { + var l = (IndexExpression)left; + var indexObj = _eval.Evaluate(l.Index); + foreach (var obj in _eval.Evaluate(l.Target)) { + obj.SetIndex(assignStmt, _unit, indexObj, values); + } + } else if (left is SequenceExpression) { + // list/tuple + var l = (SequenceExpression)left; + foreach (var value in values.ToArray()) { + for (int i = 0; i < l.Items.Count; i++) { + AssignTo(assignStmt, l.Items[i], value.GetIndex(assignStmt, _unit, ProjectState.GetConstant(i))); + } + } + } + } + + public override bool Walk(AssignmentStatement node) { + var valueType = _eval.Evaluate(node.Right); + foreach (var left in node.Left) { + AssignTo(node, left, valueType); + } + return false; + } + + public override bool Walk(AugmentedAssignStatement node) { + var right = _eval.Evaluate(node.Right); + + foreach (var x in _eval.Evaluate(node.Left)) { + x.AugmentAssign(node, _unit, right); + } + return false; + } + + public override bool Walk(ClassDefinition node) { + if (node.Body == null || node.Name == null) { + // invalid class body + return false; + } + + var newScope = LookupDefinition(node, node.Name); + if (newScope == null) { + // We failed to find the value, this occurs when there are multiple + // definitions by the same name. For example: + // class f: + // def g(): pass + // def f(): pass + // + // the 2nd f() will replace the first and we can't find g. + return false; + } + + newScope.Bases.Clear(); + // Process base classes + foreach (var baseClass in node.Bases) { + baseClass.Walk(this); + var bases = _eval.Evaluate(baseClass); + newScope.Bases.Add(bases); + } + + WalkBody(node.Body, newScope._analysisUnit); + + return false; + } + + public AnalysisUnit PushScope(AnalysisUnit unit) { + var oldUnit = _unit; + _unit = unit; + return oldUnit; + } + + public void PopScope(AnalysisUnit unit) { + _unit = unit; + } + + public override bool Walk(ExpressionStatement node) { + _eval.Evaluate(node.Expression); + return false; + } + + public override bool Walk(ForStatement node) { + foreach (var listType in _eval.Evaluate(node.List).ToArray()) { + AssignTo(node, node.Left, listType.GetEnumeratorTypes(node, _unit)); + } + + if (node.Body != null) { + node.Body.Walk(this); + } + + if (node.Else != null) { + node.Else.Walk(this); + } + return false; + } + + private void WalkFromImportWorker(FromImportStatement node, Namespace userMod, object[] mods, string impName, string newName) { + var saveName = (newName == null) ? impName : newName; + GlobalScope.Imports[node].Types.Add(new[] { impName, newName }); + + // TODO: Better node would be the name node but we don't have a name node (they're just strings in the AST w/ no position info) + var variable = Scopes[Scopes.Length - 1].CreateVariable(node, _unit, saveName); + + ISet newTypes = EmptySet.Instance; + bool madeSet = false; + + // look for builtin / user-defined modules first + ModuleInfo module = userMod as ModuleInfo; + if (module != null) { + var modVal = module.Scope.CreateVariable(node, _unit, impName); + modVal.AddDependency(_unit); + + newTypes = newTypes.Union(modVal.Types, ref madeSet); + } + + BuiltinModule builtinModule = userMod as BuiltinModule; + if (builtinModule != null) { + var modVal = builtinModule.GetMember(node, _unit, impName); + + newTypes = newTypes.Union(modVal, ref madeSet); + } + + // then look for .NET reflected namespace + if (mods != null) { + var mems = new List(mods.Length); + foreach (var mod in mods) { + object val; + if (ProjectState.TryGetMember(mod, impName, out val) && val != null) { + mems.Add(val); + } + } + + if (mems.Count > 0) { + GlobalScope.ShowClr = true; + var ns = ProjectState.GetNamespaceFromObjects(mems); + newTypes = newTypes.Union(ns.SelfSet, ref madeSet); + } + } + + variable.AddTypes(node, _unit, newTypes); + } + + public override bool Walk(FromImportStatement node) { + var mods = ProjectState.GetReflectedNamespaces(node.Root.Names, true); + ModuleReference moduleRef; + Namespace userMod = null; + var modName = node.Root.MakeString(); + if (ProjectState.Modules.TryGetValue(modName, out moduleRef)) { + userMod = moduleRef.Module; + if (userMod == null) { + moduleRef.References.Add(_unit); + } + } else { + moduleRef = ProjectState.Modules[modName] = new ModuleReference(); + if (moduleRef.References == null) { + moduleRef.References = new HashSet(); + } + moduleRef.References.Add(_unit); + } + + var asNames = node.AsNames ?? node.Names; + var impInfo = new ImportInfo(node.Root.MakeString(), node.Span); + GlobalScope.Imports[node] = impInfo; + + int len = Math.Min(node.Names.Count, asNames.Count); + for (int i = 0; i < len; i++) { + var impName = node.Names[i]; + var newName = asNames[i]; + + if (impName == null) { + // incomplete import statement + continue; + } else if (impName == "*") { + // Handle "import *" + if (userMod != null) { + foreach (var varName in GetModuleKeys(userMod)) { + WalkFromImportWorker(node, userMod, mods, varName, null); + } + } + if (mods != null) { + foreach (var mod in mods) { + foreach (var name in Utils.DirHelper(mod, true)) { + WalkFromImportWorker(node, null, mods, name, null); + } + } + } + } else { + WalkFromImportWorker(node, userMod, mods, impName, newName); + } + } + + return true; + } + + private ICollection GetModuleKeys(Namespace userMod) { + ModuleInfo mi = userMod as ModuleInfo; + if (mi != null) { + return mi.Scope.Variables.Keys; + } + + BuiltinModule bmi = userMod as BuiltinModule; + if (bmi != null) { + return bmi.VariableDict.Keys; + } + + return new string[0]; + } + + private List LookupBaseMethods(string name, IEnumerable> bases, Node node, AnalysisUnit unit) { + var result = new List(); + foreach (var b in bases) { + foreach (var curType in b) { + BuiltinClassInfo klass = curType as BuiltinClassInfo; + if (klass != null) { + var value = klass.GetMember(node, unit, "name"); // curType.GetVariable(name); + if (value != null) { + result.AddRange(value); + } + } + } + } + return result; + } + + private void PropagateBaseParams(FunctionInfo newScope, Namespace method) { + foreach (var overload in method.Overloads) { + var p = overload.Parameters; + if (p.Length == newScope.ParameterTypes.Length) { + for (int i = 1; i < p.Length; i++) { + var baseParam = p[i]; + var newParam = newScope.ParameterTypes[i]; + // TODO: baseParam.Type isn't right, it's a string, not a type object + var baseType = ProjectState.GetNamespaceFromObjects(baseParam.Type); + if (baseType != null) { + newParam.Types.Add(baseType); + } + } + } + } + } + + private void ProcessFunctionDecorators(FunctionDefinition funcdef, FunctionInfo newScope) { + if (funcdef.Decorators != null) { + foreach (var d in funcdef.Decorators) { + var decorator = _eval.Evaluate(d); + + if (decorator.Contains(ProjectState._propertyObj)) { + newScope.IsProperty = true; + } else if (decorator.Contains(ProjectState._staticmethodObj)) { + newScope.IsStatic = true; + } else if (decorator.Contains(ProjectState._classmethodObj)) { + newScope.IsClassMethod = true; + } + } + } + + if (newScope.IsClassMethod) { + if (newScope.ParameterTypes.Length > 0) { + newScope.ParameterTypes[0].AddTypes(funcdef.Parameters[0], _unit, ProjectState._typeObj.SelfSet); + } + } else if (!newScope.IsStatic) { + // self is always an instance of the class + // TODO: Check for __new__ (auto static) and + // @staticmethod and @classmethod and @property + InstanceInfo selfInst = null; + for (int i = Scopes.Length - 1; i >= 0; i--) { + if (Scopes[i] is ClassScope) { + selfInst = ((ClassScope)Scopes[i]).Class.Instance; + break; + } + } + if (selfInst != null && newScope.ParameterTypes.Length > 0) { + newScope.ParameterTypes[0].AddTypes(funcdef.Parameters[0], _unit, selfInst.SelfSet); + } + } + } + + public override bool Walk(FunctionDefinition node) { + if (node.Body == null) { + // invalid function body + return false; + } + + var newScope = this._unit.DeclaringModule.NodeVariables[node].First() as FunctionInfo; + Debug.Assert(newScope != null); + + // TODO: __new__ in class should assign returnValue + + var curClass = CurrentClass; + if (curClass != null) { + // wire up information about the class + // TODO: Should follow MRO + var bases = LookupBaseMethods(node.Name, curClass.Class.Bases, node, _unit); + foreach (var ns in bases) { + if (ns is BuiltinMethodInfo) { + PropagateBaseParams(newScope, ns); + } + } + } + ProcessFunctionDecorators(node, newScope); + + // process parameters + int len = Math.Min(node.Parameters.Count, newScope.ParameterTypes.Length); + for (int i = 0; i < len; i++) { + var p = node.Parameters[i]; + var v = newScope.ParameterTypes[i]; + if (p.DefaultValue != null) { + var val = _eval.Evaluate(p.DefaultValue); + if (val != null) { + v.AddTypes(p, _unit, val); + } + } + } + + // process body + WalkBody(node.Body, newScope._analysisUnit); + + return false; + } + + private void WalkBody(Node node, AnalysisUnit unit) { + var oldUnit = _unit; + var eval = _eval; + _unit = unit; + _eval = new ExpressionEvaluator(unit); + try { + node.Walk(this); + } finally { + _unit = oldUnit; + _eval = eval; + } + } + + public override bool Walk(IfStatement node) { + foreach (var test in node.Tests) { + _eval.Evaluate(test.Test); + test.Body.Walk(this); + } + if (node.ElseStatement != null) { + node.ElseStatement.Walk(this); + } + return true; + } + + public override bool Walk(ImportStatement node) { + var iinfo = new ImportInfo("", node.Span); + GlobalScope.Imports[node] = iinfo; + int len = Math.Min(node.Names.Count, node.AsNames.Count); + for (int i = 0; i < len; i++) { + var impNode = node.Names[i]; + var newName = node.AsNames[i]; + var strImpName = impNode.MakeString(); + iinfo.Types.Add(new[] { strImpName, newName }); + + if (strImpName == "clr") { + GlobalScope.ShowClr = true; + } else if (strImpName == "wpf") { + AddWpfReferences(); + } + + var saveName = (String.IsNullOrEmpty(newName)) ? strImpName : newName; + ModuleReference modRef; + + var def = Scopes[Scopes.Length - 1].CreateVariable(impNode, _unit, saveName); + + if (ProjectState.Modules.TryGetValue(strImpName, out modRef)) { + if (modRef.Module != null) { + ModuleInfo mi = modRef.Module as ModuleInfo; + if (mi != null) { + mi.ModuleDefinition.AddDependency(_unit); + } + def.AddTypes(impNode, _unit, modRef.Module.SelfSet); + continue; + } else { + modRef.References.Add(_unit); + } + } else { + ProjectState.Modules[strImpName] = modRef = new ModuleReference(); + if (modRef.References == null) { + modRef.References = new HashSet(); + } + modRef.References.Add(_unit); + } + + var builtinRefs = ProjectState.GetReflectedNamespaces(impNode.Names, impNode.Names.Count > 1 && !String.IsNullOrEmpty(newName)); + if (builtinRefs != null && builtinRefs.Length > 0) { + GlobalScope.ShowClr = true; + var ns = ProjectState.GetNamespaceFromObjects(builtinRefs); + + // TODO: Should we pony up a fake module for the module we failed to resolve? + if (ns != null) { + def.AddTypes(impNode, _unit, ns.SelfSet); + } + } else { + + } + } + return true; + } + + private void AddWpfReferences() { + ProjectState.AddAssembly(typeof(System.Windows.Markup.XamlReader).Assembly); // PresentationFramework + ProjectState.AddAssembly(typeof(System.Windows.Clipboard).Assembly); // PresentationCore + ProjectState.AddAssembly(typeof(System.Windows.DependencyProperty).Assembly); // WindowsBase + ProjectState.AddAssembly(typeof(System.Xaml.XamlReader).Assembly); // System.Xaml + } + + public override bool Walk(ReturnStatement node) { + var curFunc = CurrentFunction; + if (node.Expression != null && curFunc != null) { + var lookupRes = _eval.Evaluate(node.Expression); + + var retVal = curFunc.Function.ReturnValue; + int typeCount = retVal.Types.Count; + foreach (var type in lookupRes) { + retVal.AddTypes(node, _unit, type); + } + if (typeCount != retVal.Types.Count) { + retVal.EnqueueDependents(); + } + } + return true; + } + + public override bool Walk(WithStatement node) { + var ctxMgr = _eval.Evaluate(node.ContextManager); + if (node.Variable != null) { + AssignTo(node, node.Variable, ctxMgr); + } + return true; + } + + public override bool Walk(PrintStatement node) { + foreach (var expr in node.Expressions) { + _eval.Evaluate(expr); + } + return false; + } + + public override bool Walk(AssertStatement node) { + _eval.EvaluateMaybeNull(node.Test); + _eval.EvaluateMaybeNull(node.Message); + return false; + } + + public override bool Walk(DelStatement node) { + foreach (var expr in node.Expressions) { + DeleteExpression(expr); + } + return false; + } + + private void DeleteExpression(Expression expr) { + NameExpression name = expr as NameExpression; + if (name != null) { + var variable = _eval.LookupVariableByName(name.Name, expr); + if (variable != null) { + variable.AddReference(name, _unit); + } + } + + IndexExpression index = expr as IndexExpression; + if (index != null) { + var values = _eval.Evaluate(index.Target); + var indexValues = _eval.Evaluate(index.Index); + foreach (var value in values) { + value.DeleteIndex(index, _unit, indexValues); + } + } + + MemberExpression member = expr as MemberExpression; + if (member != null) { + var values = _eval.Evaluate(member.Target); + foreach (var value in values) { + value.DeleteMember(member, _unit, member.Name); + } + } + + ParenthesisExpression paren = expr as ParenthesisExpression; + if (paren != null) { + DeleteExpression(paren.Expression); + } + + SequenceExpression seq = expr as SequenceExpression; + if (seq != null) { + foreach (var item in seq.Items) { + DeleteExpression(item); + } + } + } + + public override bool Walk(RaiseStatement node) { + _eval.EvaluateMaybeNull(node.Value); + _eval.EvaluateMaybeNull(node.Traceback); + _eval.EvaluateMaybeNull(node.ExceptType); + return false; + } + + public override bool Walk(WhileStatement node) { + _eval.Evaluate(node.Test); + + node.Body.Walk(this); + if (node.ElseStatement != null) { + node.ElseStatement.Walk(this); + } + + return false; + } + + public override bool Walk(TryStatement node) { + node.Body.Walk(this); + if (node.Handlers != null) { + foreach (var handler in node.Handlers) { + ISet test = EmptySet.Instance; + bool madeSet = false; + if (handler.Test != null) { + var testTypes = _eval.Evaluate(handler.Test); + + if (handler.Target != null) { + foreach (var type in testTypes) { + ClassInfo klass = type as ClassInfo; + if (klass != null) { + test = test.Union(klass.Instance.SelfSet, ref madeSet); + } + + BuiltinClassInfo builtinClass = type as BuiltinClassInfo; + if (builtinClass != null) { + test = test.Union(builtinClass.Instance.SelfSet, ref madeSet); + } + } + + AssignTo(handler, handler.Target, test); + } + } + + handler.Body.Walk(this); + } + } + + if (node.Finally != null) { + node.Finally.Walk(this); + } + + if (node.Else != null) { + node.Else.Walk(this); + } + + return false; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/EmptySet.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/EmptySet.cs new file mode 100644 index 0000000000..0bad838f8f --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/EmptySet.cs @@ -0,0 +1,157 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; + +namespace Microsoft.PyAnalysis { + sealed class EmptySet : ISet { + private static readonly IEnumerator EmptyEnum = new EmptyEnumerator(); + public static readonly EmptySet Instance = new EmptySet(); + + private EmptySet() { + } + + #region ISet Members + + public bool Add(T item) { + throw new InvalidOperationException(); + } + + public void ExceptWith(IEnumerable other) { + throw new InvalidOperationException(); + } + + public void IntersectWith(IEnumerable other) { + throw new InvalidOperationException(); + } + + public bool IsProperSubsetOf(IEnumerable other) { + return true; + } + + public bool IsProperSupersetOf(IEnumerable other) { + return false; + } + + public bool IsSubsetOf(IEnumerable other) { + return true; + } + + public bool IsSupersetOf(IEnumerable other) { + return false; + } + + public bool Overlaps(IEnumerable other) { + return false; + } + + public bool SetEquals(IEnumerable other) { + foreach (T x in other) { + return false; + } + return true; + } + + public void SymmetricExceptWith(IEnumerable other) { + throw new InvalidOperationException(); + } + + public void UnionWith(IEnumerable other) { + throw new InvalidOperationException(); + } + + #endregion + + #region ICollection Members + + void ICollection.Add(T item) { + throw new InvalidOperationException(); + } + + public void Clear() { + } + + public bool Contains(T item) { + return false; + } + + public void CopyTo(T[] array, int arrayIndex) { + } + + public int Count { + get { return 0; } + } + + public bool IsReadOnly { + get { return true; } + } + + public bool Remove(T item) { + throw new InvalidOperationException(); + } + + #endregion + + #region IEnumerable Members + + public IEnumerator GetEnumerator() { + return EmptyEnum; + } + + #endregion + + #region IEnumerable Members + + IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return EmptyEnum; + } + + #endregion + + class EmptyEnumerator : IEnumerator { + #region IEnumerator Members + + public T Current { + get { throw new NotImplementedException(); } + } + + #endregion + + #region IDisposable Members + + public void Dispose() { + } + + #endregion + + #region IEnumerator Members + + object IEnumerator.Current { + get { throw new InvalidOperationException(); } + } + + public bool MoveNext() { + return false; + } + + public void Reset() { + } + + #endregion + } + + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/ExpressionEvaluator.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/ExpressionEvaluator.cs new file mode 100644 index 0000000000..270188155f --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/ExpressionEvaluator.cs @@ -0,0 +1,389 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using IronPython.Compiler.Ast; +using IronPython.Runtime; +using Microsoft.PyAnalysis.Values; +using Microsoft.Scripting.Utils; + +namespace Microsoft.PyAnalysis.Interpreter { + internal class ExpressionEvaluator { + private readonly AnalysisUnit _unit; + private readonly InterpreterScope[] _currentScopes; + + /// + /// Creates a new ExpressionEvaluator that will evaluate in the context of the top-level module. + /// + public ExpressionEvaluator(AnalysisUnit unit) { + _unit = unit; + _currentScopes = unit.Scopes; + } + + public ExpressionEvaluator(AnalysisUnit unit, InterpreterScope[] scopes) { + _unit = unit; + _currentScopes = scopes; + } + + #region Public APIs + + /// + /// Returns possible variable refs associated with the expr in the expression evaluators scope. + /// + public ISet Evaluate(Expression node) { + var res = EvaluateWorker(node); + Debug.Assert(res != null); + return res; + } + + public ISet EvaluateMaybeNull(Expression node) { + if (node == null) { + return null; + } + + return Evaluate(node); + } + + /// + /// Returns a sequence of possible types associated with the name in the expression evaluators scope. + /// + public ISet LookupNamespaceByName(Node node, string name, bool addRef = true) { + for (int i = Scopes.Length - 1; i >= 0; i--) { + if (i == Scopes.Length - 1 || Scopes[i].VisibleToChildren) { + var refs = Scopes[i].GetVariable(node, _unit, name, addRef); + if (refs != null) { + return refs.Types; + } + } + } + + return ProjectState.BuiltinModule.GetMember(node, _unit, name); + } + + /// + /// Returns the variable definition for the given name. + /// + public VariableDef LookupVariableByName(string name, Node node, bool addReference = true) { + for (int i = Scopes.Length - 1; i >= 0; i--) { + if (i == Scopes.Length - 1 || Scopes[i].VisibleToChildren) { + var value = Scopes[i].GetVariable(node, _unit, name, addReference); + if (value != null) { + return value; + } + } + } + + return null; + } + + #endregion + + #region Implementation Details + + private ModuleInfo GlobalScope { + get { return _unit.DeclaringModule; } + } + + private ProjectState ProjectState { + get { return _unit.ProjectState; } + } + + /// + /// Gets the list of scopes which define the current context. + /// + private InterpreterScope[] Scopes { + get { return _currentScopes; } + } + + private ISet[] Evaluate(IList nodes) { + var result = new ISet[nodes.Count]; + for (int i = 0; i < nodes.Count; i++) { + result[i] = Evaluate(nodes[i].Expression); + } + return result; + } + + private ISet EvaluateWorker(Node node) { + EvalDelegate eval; + if (_evaluators.TryGetValue(node.GetType(), out eval)) { + return eval(this, node); + } + + return EmptySet.Instance; + } + + delegate ISet EvalDelegate(ExpressionEvaluator ee, Node node); + + private static Dictionary _evaluators = new Dictionary { + { typeof(AndExpression), ExpressionEvaluator.EvaluateAnd }, + { typeof(BackQuoteExpression), ExpressionEvaluator.EvaluateBackQuote }, + { typeof(BinaryExpression), ExpressionEvaluator.EvaluateBinary }, + { typeof(CallExpression), ExpressionEvaluator.EvaluateCall}, + { typeof(ConditionalExpression), ExpressionEvaluator.EvaluateConditional}, + { typeof(ConstantExpression), ExpressionEvaluator.EvaluateConstant}, + { typeof(DictionaryExpression), ExpressionEvaluator.EvaluateDictionary}, + { typeof(SetExpression), ExpressionEvaluator.EvaluateSet}, + { typeof(GeneratorExpression), ExpressionEvaluator.EvaluateGenerator}, + { typeof(IndexExpression), ExpressionEvaluator.EvaluateIndex}, + { typeof(LambdaExpression), ExpressionEvaluator.EvaluateLambda}, + { typeof(ListComprehension), ExpressionEvaluator.EvaluateListComprehension}, + { typeof(MemberExpression), ExpressionEvaluator.EvaluateMember}, + { typeof(NameExpression), ExpressionEvaluator.EvaluateName}, + { typeof(OrExpression), ExpressionEvaluator.EvaluateOr}, + { typeof(ParenthesisExpression), ExpressionEvaluator.EvaluateParenthesis}, + { typeof(UnaryExpression), ExpressionEvaluator.EvaluateUnary }, + { typeof(YieldExpression), ExpressionEvaluator.EvaluateYield}, + { typeof(TupleExpression), ExpressionEvaluator.EvaluateSequence}, + { typeof(ListExpression), ExpressionEvaluator.EvaluateSequence}, + { typeof(SliceExpression), ExpressionEvaluator.EvaluateSlice}, + }; + + private static ISet EvaluateSequence(ExpressionEvaluator ee, Node node) { + // Covers both ListExpression and TupleExpression + return ee.GlobalScope.GetOrMakeNodeVariable(node, (n) => ee.MakeSequence(ee, n)); + } + + private static ISet EvaluateParenthesis(ExpressionEvaluator ee, Node node) { + var n = (ParenthesisExpression)node; + return ee.Evaluate(n.Expression); + } + + private static ISet EvaluateOr(ExpressionEvaluator ee, Node node) { + // TODO: Warn if lhs is always false + var n = (OrExpression)node; + var result = ee.Evaluate(n.Left); + return result.Union(ee.Evaluate(n.Right)); + } + + private static ISet EvaluateName(ExpressionEvaluator ee, Node node) { + var n = (NameExpression)node; + var res = ee.LookupNamespaceByName(node, n.Name); + foreach (var value in res) { + value.AddReference(node, ee._unit); + } + return res; + } + + private static ISet EvaluateMember(ExpressionEvaluator ee, Node node) { + var n = (MemberExpression)node; + return ee.Evaluate(n.Target).GetMember(node, ee._unit, n.Name); + } + + private static ISet EvaluateIndex(ExpressionEvaluator ee, Node node) { + var n = (IndexExpression)node; + + return ee.Evaluate(n.Target).GetIndex(n, ee._unit, ee.Evaluate(n.Index)); + } + + private static ISet EvaluateSet(ExpressionEvaluator ee, Node node) { + var n = (SetExpression)node; + ISet result; + if (!ee.GlobalScope.NodeVariables.TryGetValue(node, out result)) { + var values = new HashSet(); + foreach (var x in n.Items) { + values.Union(ee.Evaluate(x)); + } + + result = new DictionaryInfo(values, values, ee.ProjectState, ee.GlobalScope.ShowClr).SelfSet; + ee.GlobalScope.NodeVariables[node] = result; + } + return result; + } + + private static ISet EvaluateDictionary(ExpressionEvaluator ee, Node node) { + var n = (DictionaryExpression)node; + ISet result; + if (!ee.GlobalScope.NodeVariables.TryGetValue(node, out result)) { + var keys = new HashSet(); + var values = new HashSet(); + foreach (var x in n.Items) { + foreach (var keyVal in ee.Evaluate(x.SliceStart)) { + keys.Add(keyVal); + } + foreach (var itemVal in ee.Evaluate(x.SliceStop)) { + values.Add(itemVal); + } + } + + result = new DictionaryInfo(keys, values, ee.ProjectState, ee.GlobalScope.ShowClr).SelfSet; + ee.GlobalScope.NodeVariables[node] = result; + } + return result; + } + + private static ISet EvaluateConstant(ExpressionEvaluator ee, Node node) { + var n = (ConstantExpression)node; + return ee.ProjectState.GetConstant(n.Value); + } + + private static ISet EvaluateConditional(ExpressionEvaluator ee, Node node) { + var n = (ConditionalExpression)node; + ee.Evaluate(n.Test); + var result = ee.Evaluate(n.TrueExpression); + return result.Union(ee.Evaluate(n.FalseExpression)); + } + + private static ISet EvaluateBackQuote(ExpressionEvaluator ee, Node node) { + var strType = ee.ProjectState.GetNamespaceFromObjects(typeof(string)); + return strType.SelfSet; + } + + private static ISet EvaluateAnd(ExpressionEvaluator ee, Node node) { + var n = (AndExpression)node; + var result = ee.Evaluate(n.Left); + return result.Union(ee.Evaluate(n.Right)); + } + + private static ISet EvaluateCall(ExpressionEvaluator ee, Node node) { + // TODO: Splatting, keyword args + + // Get the argument types that we're providing at this call site + var n = (CallExpression)node; + var argTypes = ee.Evaluate(n.Args); + + // Then lookup the possible methods we're calling + var targetRefs = ee.Evaluate(n.Target); + + ISet res = EmptySet.Instance; + bool madeSet = false; + foreach (var target in targetRefs) { + res = res.Union(target.Call(node, ee._unit, argTypes, GetNamedArguments(n.Args)), ref madeSet); + } + + return res; + } + + + private static string[] GetNamedArguments(IList args) { + string[] res = null; + for (int i = 0; i < args.Count; i++) { + if (args[i].Name != null) { + if (res == null) { + res = new string[args.Count - i]; + } + + res[i - (args.Count - res.Length)] = args[i].Name; + } + } + return res ?? ArrayUtils.EmptyStrings; + } + + private static ISet EvaluateUnary(ExpressionEvaluator ee, Node node) { + var n = (UnaryExpression)node; + return ee.Evaluate(n.Expression).UnaryOperation(node, ee._unit, n.Op); ; + } + + private static ISet EvaluateBinary(ExpressionEvaluator ee, Node node) { + var n = (BinaryExpression)node; + + return ee.Evaluate(n.Left).BinaryOperation(node, ee._unit, n.Operator, ee.Evaluate(n.Right)); + } + + private static ISet EvaluateYield(ExpressionEvaluator ee, Node node) { + var yield = (YieldExpression)node; + var funcDef = ee._currentScopes[ee._currentScopes.Length - 1].Namespace as FunctionInfo; + if (funcDef != null) { + var gen = funcDef.Generator; + + gen.AddYield(ee.Evaluate(yield.Expression)); + + return gen.Sends.Types; + } + + return EmptySet.Instance; + } + + private static ISet EvaluateListComprehension(ExpressionEvaluator ee, Node node) { + ListComprehension listComp = (ListComprehension)node; + + for(int i = 0; i new ListInfo(new[] { ee.Evaluate(listComp.Item) }, ee._unit.ProjectState._listType).SelfSet);*/ + + return ee.GlobalScope.GetOrMakeNodeVariable( + node, + (x) => new ListInfo(new ISet[0], ee._unit.ProjectState._listType).SelfSet); + } + + private static ISet EvaluateLambda(ExpressionEvaluator ee, Node node) { + var lambda = (LambdaExpression)node; + + return ee.GlobalScope.GetOrMakeNodeVariable(node, n => MakeLambdaFunction(lambda, ee)); + } + + private static ISet MakeLambdaFunction(LambdaExpression node, ExpressionEvaluator ee) { + return ee.GlobalScope.NodeVariables[node.Function]; + } + + private static ISet EvaluateGenerator(ExpressionEvaluator ee, Node node) { + GeneratorExpression gen = (GeneratorExpression)node; + + ee.Evaluate(gen.Iterable); + + // TODO: Implement + return EmptySet.Instance; + } + + private static ISet EvaluateSlice(ExpressionEvaluator ee, Node node) { + SliceExpression se = node as SliceExpression; + + return ee.GlobalScope.GetOrMakeNodeVariable( + node, + (n) => new SliceInfo( + ee.EvaluateMaybeNull(se.SliceStart), + ee.EvaluateMaybeNull(se.SliceStop), + se.StepProvided ? ee.EvaluateMaybeNull(se.SliceStep) : null + ) + ); + } + + private ISet MakeSequence(ExpressionEvaluator ee, Node node) { + ISet result; + if (!ee.GlobalScope.NodeVariables.TryGetValue(node, out result)) { + var seqItems = ((SequenceExpression)node).Items; + var indexValues = new ISet[seqItems.Count]; + + for (int i = 0; i < seqItems.Count; i++) { + indexValues[i] = Evaluate(seqItems[i]); + } + + ISet sequence; + if (node is ListExpression) { + sequence = new ListInfo(indexValues, _unit.ProjectState._listType).SelfSet; + } else { + Debug.Assert(node is TupleExpression); + sequence = new SequenceInfo(indexValues, _unit.ProjectState._tupleType).SelfSet; + } + + ee.GlobalScope.NodeVariables[node] = result = sequence; + } + + return result; + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/FunctionScope.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/FunctionScope.cs new file mode 100644 index 0000000000..dced9fe82d --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/FunctionScope.cs @@ -0,0 +1,38 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Values; + +namespace Microsoft.PyAnalysis.Interpreter { + class FunctionScope : InterpreterScope { + public FunctionScope(FunctionInfo functionInfo) + : base(functionInfo) { + } + + public FunctionInfo Function { + get { + return Namespace as FunctionInfo; + } + } + + public override string Name { + get { return Function.FunctionDefinition.Name; } + } + + public VariableDef DefineVariable(Parameter node, AnalysisUnit unit) { + return Variables[node.Name] = new LocatedVariableDef(unit.DeclaringModule.ProjectEntry, node); + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/InterpreterScope.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/InterpreterScope.cs new file mode 100644 index 0000000000..02528c4eb5 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/InterpreterScope.cs @@ -0,0 +1,89 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Values; + +namespace Microsoft.PyAnalysis.Interpreter { + abstract class InterpreterScope { + private readonly Namespace _ns; + private Dictionary _variables = new Dictionary(); + + public InterpreterScope(Namespace ns) { + _ns = ns; + } + + public abstract string Name { + get; + } + + public void SetVariable(Node node, AnalysisUnit unit, string name, IEnumerable value, bool addRef = true) { + var variable = CreateVariable(node, unit, name, false); + + variable.AddTypes(node, unit, value); + if (addRef) { + variable.AddAssignment(node, unit); + } + } + + public VariableDef GetVariable(Node node, AnalysisUnit unit, string name, bool addRef = true) { + VariableDef res; + if (_variables.TryGetValue(name, out res)) { + if (addRef) { + res.AddReference(node, unit); + } + return res; + } + return null; + } + + public VariableDef CreateVariable(Node node, AnalysisUnit unit, string name, bool addRef = true) { + var res = GetVariable(node, unit, name, addRef); + if (res == null) { + _variables[name] = res = new VariableDef(); + if (addRef) { + res.AddReference(node, unit); + } + } + return res; + } + + protected VariableDef CreateVariableWorker(Node node, AnalysisUnit unit, string name) { + VariableDef res; + if (!_variables.TryGetValue(name, out res)) { + _variables[name] = res = new VariableDef(); + } + return res; + } + + public IDictionary Variables { + get { + return _variables; + } + } + + public virtual bool VisibleToChildren { + get { + return true; + } + } + + public Namespace Namespace { + get { + return _ns; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/ModuleScope.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/ModuleScope.cs new file mode 100644 index 0000000000..653b7fd0fa --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/ModuleScope.cs @@ -0,0 +1,30 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using Microsoft.PyAnalysis.Values; + +namespace Microsoft.PyAnalysis.Interpreter { + class ModuleScope : InterpreterScope { + + public ModuleScope(ModuleInfo moduleInfo) + : base(moduleInfo) { + } + + public ModuleInfo Module { get { return Namespace as ModuleInfo; } } + + public override string Name { + get { return Module.Name; } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/OverviewWalker.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/OverviewWalker.cs new file mode 100644 index 0000000000..e33fa85f29 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/OverviewWalker.cs @@ -0,0 +1,134 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Values; + +namespace Microsoft.PyAnalysis.Interpreter { + /// + /// Performs the 1st pass over the AST to gather all of the classes and + /// function definitions. + /// + internal class OverviewWalker : PythonWalker { + private readonly List _scopes; + private readonly ProjectEntry _entry; + private readonly Stack _scopeTree; + private readonly Stack _analysisStack = new Stack(); + private AnalysisUnit _curUnit; + + public OverviewWalker(ProjectEntry entry, AnalysisUnit topAnalysis) { + _entry = entry; + _curUnit = topAnalysis; + + _scopes = new List(); + _scopes.Push(entry.MyScope.Scope); + + _scopeTree = new Stack(); + _scopeTree.Push(new ScopePositionInfo(1, Int32.MaxValue, null)); + } + + // TODO: What about names being redefined? + // remember classes/functions as they start new scopes + public override bool Walk(ClassDefinition node) { + if (node.Body == null || node.Name == null) { + return false; + } + + var queue = _entry.ProjectState.Queue; + + var scopes = new InterpreterScope[_scopes.Count + 1]; + _scopes.CopyTo(scopes); + + _analysisStack.Push(_curUnit); + var unit = _curUnit = new AnalysisUnit(node, scopes, _curUnit); + var klass = new ClassInfo(unit, _entry); + var classScope = klass.Scope; + + var scope = _scopes.Peek(); + scope.SetVariable(node, unit, node.Name, klass.SelfSet); + + _scopes.Push(classScope); + scopes[scopes.Length - 1] = classScope; + + // TODO: Add parameters for __new__/__init__ + PushPositionScope(node, classScope); + + return true; + } + + public override void PostWalk(ClassDefinition node) { + if (node.Body != null && node.Name != null) { + _scopes.Pop(); + _scopeTree.Pop(); + _curUnit = _analysisStack.Pop(); + } + } + + public override bool Walk(FunctionDefinition node) { + if (node.Body == null || node.Name == null) { + return false; + } + + var queue = _entry.ProjectState.Queue; + var scopes = new InterpreterScope[_scopes.Count + 1]; + _scopes.CopyTo(scopes); + + _analysisStack.Push(_curUnit); + var unit = _curUnit = new AnalysisUnit(node, scopes, _curUnit); + var function = new FunctionInfo(unit, _entry); + var funcScope = new FunctionScope(function); + + _entry.MyScope.GetOrMakeNodeVariable(node, x => function.SelfSet); + _scopes.Push(funcScope); + scopes[scopes.Length - 1] = funcScope; + + if (!node.IsLambda) { + // lambdas don't have their names published + var scope = _scopes[_scopes.Count - 2]; + scope.SetVariable(node, unit, node.Name, function.SelfSet); + } + + var newParams = new VariableDef[node.Parameters.Count]; + int index = 0; + foreach (var param in node.Parameters) { + newParams[index++] = funcScope.DefineVariable(param, _curUnit); + } + function.SetParameters(newParams); + + PushPositionScope(node, funcScope); + + return true; + } + + public override void PostWalk(FunctionDefinition node) { + if (node.Body != null && node.Name != null) { + _scopes.Pop(); + _scopeTree.Pop(); + _curUnit = _analysisStack.Pop(); + } + } + + private void PushPositionScope(Node node, InterpreterScope newScope) { + var newPositionInfo = new ScopePositionInfo(node.Start.Line, node.End.Line, newScope); + _scopeTree.Peek().Children.Add(newPositionInfo); + _scopeTree.Push(newPositionInfo); + } + + public Stack ScopeTree { + get { return _scopeTree; } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/ScopePositionInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/ScopePositionInfo.cs new file mode 100644 index 0000000000..70c8164dca --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/ScopePositionInfo.cs @@ -0,0 +1,37 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.PyAnalysis.Values; + +namespace Microsoft.PyAnalysis.Interpreter { + /// + /// Holds information which allows us to quickly discover the relevant + /// scopes for a given piece of code. This includes the linear start & stop + /// position, the associated scope with that position, and any children. + /// + internal class ScopePositionInfo { + public readonly int Start; + public readonly int Stop; + public readonly InterpreterScope Scope; + public readonly List Children; + + public ScopePositionInfo(int start, int stop, InterpreterScope scope) { + Start = start; + Stop = stop; + Scope = scope; + Children = new List(); + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/SetOfOne.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/SetOfOne.cs new file mode 100644 index 0000000000..1f6ba07f81 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/SetOfOne.cs @@ -0,0 +1,181 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; + +namespace Microsoft.PyAnalysis { +#if FALSE + // No longer used but kept around just in case - now incorporated into Namespace + class SetOfOne : ISet { + private readonly T _value; + + public SetOfOne(T value) { + _value = value; + } + + public T Value { + get { + return _value; + } + } + + #region ISet Members + + public bool Add(T item) { + throw new InvalidOperationException(); + } + + public void ExceptWith(IEnumerable other) { + throw new InvalidOperationException(); + } + + public void IntersectWith(IEnumerable other) { + throw new InvalidOperationException(); + } + + public bool IsProperSubsetOf(IEnumerable other) { + throw new NotImplementedException(); + } + + public bool IsProperSupersetOf(IEnumerable other) { + throw new NotImplementedException(); + } + + public bool IsSubsetOf(IEnumerable other) { + throw new NotImplementedException(); + } + + public bool IsSupersetOf(IEnumerable other) { + throw new NotImplementedException(); + } + + public bool Overlaps(IEnumerable other) { + throw new NotImplementedException(); + } + + public bool SetEquals(IEnumerable other) { + var enumerator = other.GetEnumerator(); + if (enumerator.MoveNext()) { + if (Contains(enumerator.Current)) { + return !enumerator.MoveNext(); + } + } + return false; + } + + public void SymmetricExceptWith(IEnumerable other) { + throw new InvalidOperationException(); + } + + public void UnionWith(IEnumerable other) { + throw new InvalidOperationException(); + } + + #endregion + + #region ICollection Members + + void ICollection.Add(T item) { + throw new NotImplementedException(); + } + + public void Clear() { + throw new InvalidOperationException(); + } + + public bool Contains(T item) { + return EqualityComparer.Default.Equals(item, _value); + } + + public void CopyTo(T[] array, int arrayIndex) { + array[arrayIndex] = _value; + } + + public int Count { + get { return 1; } + } + + public bool IsReadOnly { + get { return true; } + } + + public bool Remove(T item) { + throw new InvalidOperationException(); + } + + #endregion + + #region IEnumerable Members + + public IEnumerator GetEnumerator() { + return new SetOfOneEnumerator(_value); + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + yield return _value; + } + + #endregion + + class SetOfOneEnumerator : IEnumerator { + private readonly T _value; + private bool _enumerated; + + public SetOfOneEnumerator(T value) { + _value = value; + } + + #region IEnumerator Members + + T IEnumerator.Current { + get { return _value; } + } + + #endregion + + #region IDisposable Members + + void IDisposable.Dispose() { + } + + #endregion + + #region IEnumerator Members + + object System.Collections.IEnumerator.Current { + get { return _value; } + } + + bool System.Collections.IEnumerator.MoveNext() { + if (_enumerated) { + return false; + } + _enumerated = true; + return true; + } + + void System.Collections.IEnumerator.Reset() { + _enumerated = false; + } + + #endregion + } + } +#endif +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/SingleDict.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/SingleDict.cs new file mode 100644 index 0000000000..93b326f50a --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/SingleDict.cs @@ -0,0 +1,167 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Collections; + +namespace Microsoft.PyAnalysis.Interpreter { + /// + /// A simple dictionary like object which has efficient storage when there's only a single item in the dictionary. + /// + struct SingleDict : IEnumerable> { + private object _data; // Dictionary or SingleEntry + + sealed class SingleDependency { + public readonly TKey Key; + public TValue Value; + + public SingleDependency(TKey key, TValue value) { + Key = key; + Value = value; + } + } + + internal bool TryGetValue(TKey fromModule, out TValue deps) { + SingleDependency single = _data as SingleDependency; + if (single != null) { + if (single.Key.Equals(fromModule)) { + deps = single.Value; + return true; + } + deps = default(TValue); + return false; + } + + Dictionary dict = _data as Dictionary; + if (_data != null) { + return dict.TryGetValue(fromModule, out deps); + } + + deps = default(TValue); + return false; + } + + public TValue this[TKey key] { + get { + TValue res; + if (TryGetValue(key, out res)) { + return res; + } + + throw new KeyNotFoundException(); + } + set { + if (_data == null) { + _data = new SingleDependency(key, value); + return; + } + + SingleDependency single = _data as SingleDependency; + if (single != null) { + if (single.Key.Equals(key)) { + single.Value = value; + return; + } + + var data = new Dictionary(); + data[single.Key] = single.Value; + _data = data; + } + + Dictionary dict = _data as Dictionary; + if (dict == null) { + _data = dict = new Dictionary(); + } + dict[key] = value; + } + } + + internal void Remove(TKey fromModule) { + SingleDependency single = _data as SingleDependency; + if (single != null) { + if (single.Key.Equals(fromModule)) { + single = null; + } + return; + } + + Dictionary dict = _data as Dictionary; + if (_data != null) { + dict.Remove(fromModule); + } + } + + public IEnumerable Values { + get { + SingleDependency single = _data as SingleDependency; + if (single != null) { + yield return single.Value; + } + + Dictionary dict = _data as Dictionary; + if (dict != null) { + foreach (var value in dict.Values) { + yield return value; + } + } + } + } + + public int Count { + get { + SingleDependency single = _data as SingleDependency; + if (single != null) { + return 1; + } + + Dictionary dict = _data as Dictionary; + if (dict != null) { + return dict.Count; + } + + return 0; + } + } + + #region IEnumerable> Members + + public IEnumerator> GetEnumerator() { + SingleDependency single = _data as SingleDependency; + if (single != null) { + yield return new KeyValuePair(single.Key, single.Value); + } + + Dictionary dict = _data as Dictionary; + if (dict != null) { + foreach (var keyValue in dict) { + yield return keyValue; + } + } + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() { + throw new NotImplementedException(); + } + + #endregion + } + +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/TypeUnion.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/TypeUnion.cs new file mode 100644 index 0000000000..4dd9089475 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/TypeUnion.cs @@ -0,0 +1,115 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.PyAnalysis.Values; + +namespace Microsoft.PyAnalysis.Interpreter { + class TypeUnion : IEnumerable { + private HashSet _ns; + private bool _isObject; + private const int MaxUniqueNamespaces = 10; + + private static IEqualityComparer ObjectComparer = EqualityComparer.Default; + public static IEqualityComparer UnionComparer = new UnionEqualityComparer(); + + public bool Add(Namespace ns, ProjectState state) { + if (_isObject) { + return false; + } + if (_ns == null) { + _ns = new HashSet(ObjectComparer); + } + + if (_ns.Add(ns)) { + if (_ns.Count > MaxUniqueNamespaces) { + if (_ns.Comparer == ObjectComparer) { + _ns = new HashSet(_ns, UnionComparer); + } else { + // TODO: We should warn here in debug builds so see if we can improve tracking + _ns = state._objectSet; + _isObject = true; + } + } + return true; + } + + return false; + } + + public int Count { + get { + if (_ns == null) { + return 0; + } + return _ns.Count; + } + } + + public bool Contains(Namespace ns) { + if (_ns != null) { + return _ns.Contains(ns); + } + return false; + } + + public ISet ToSet() { + if (Count == 0) { + return EmptySet.Instance; + } + + return new HashSet(this); + } + + #region IEnumerable Members + + public IEnumerator GetEnumerator() { + if (_ns == null) { + return EmptySet(); + } + return _ns.GetEnumerator(); + } + + private IEnumerator EmptySet() { + yield break; + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + return _ns.GetEnumerator(); + } + + #endregion + + class UnionEqualityComparer : IEqualityComparer { + #region IEqualityComparer Members + + public bool Equals(Namespace x, Namespace y) { + return x.UnionEquals(y); + } + + public int GetHashCode(Namespace obj) { + return obj.UnionHashCode(); + } + + #endregion + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/VariableDef.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/VariableDef.cs new file mode 100644 index 0000000000..19b2e116d3 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Interpreter/VariableDef.cs @@ -0,0 +1,217 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Interpreter; +using Microsoft.Scripting.Utils; +using Microsoft.Scripting; + +namespace Microsoft.PyAnalysis.Values { + abstract class DependentData where TStorageType : DependencyInfo { + protected SingleDict _dependencies; + + public void ClearOldValues(IProjectEntry fromModule) { + TStorageType deps; + if (_dependencies.TryGetValue(fromModule, out deps)) { + if (deps.Version != fromModule.Version) { + _dependencies.Remove(fromModule); + } + } + } + + protected TStorageType GetDependentItems(IProjectEntry module) { + TStorageType result; + if (!_dependencies.TryGetValue(module, out result) || result.Version != module.Version) { + _dependencies[module] = result = NewDefinition(module.Version); + } + return result; + } + + protected abstract TStorageType NewDefinition(int version); + + /// + /// Enqueues any nodes which depend upon this type into the provided analysis queue for + /// further analysis. + /// + public void EnqueueDependents() { + foreach (var val in _dependencies.Values) { + foreach (var analysisUnit in val.DependentUnits) { + analysisUnit.Enqueue(); + } + } + } + + public void AddDependency(AnalysisUnit unit) { + if (!unit.ForEval) { + GetDependentItems(unit.DeclaringModule.ProjectEntry).AddDependentUnit(unit); + } + } + } + + class DependentData : DependentData { + protected override DependencyInfo NewDefinition(int version) { + return new DependencyInfo(version); + } + } + + /// + /// A VariableDef represents a collection of type information and dependencies + /// upon that type information. + /// + /// The collection of type information is represented by a HashSet of Namespace + /// objects. This set includes all of the types that are known to have been + /// seen for this variable. + /// + /// Dependency data is added when an one value is assigned to a variable. + /// For example for the statement: + /// + /// foo = value + /// + /// There will be a variable def for the name "foo", and "value" will evaluate + /// to a collection of namespaces. When value is assigned to + /// foo the types in value will be propagated to foo's VariableDef by a call + /// to AddDependentTypes. If value adds any new type information to foo + /// then the caller needs to re-analyze anyone who is dependent upon foo' + /// s values. If "value" was a VariableDef as well, rather than some arbitrary + /// expression, then reading "value" would have made the code being analyzed dependent + /// upon "value". After a call to AddTypes the caller needs to check the + /// return value and if new types were added (returns true) needs to re-enque it's scope. + /// + /// Dependecies are stored in a dictionary keyed off of the IProjectEntry object. + /// This is a consistent object which always represents the same module even + /// across multiple analysis. The object is versioned so that when we encounter + /// a new version all the old dependencies will be thrown away when a variable ref + /// is updated with new dependencies. + /// + /// TODO: We should store built-in types not keyed off of the ModuleInfo. + /// + class VariableDef : DependentData, IReferenceable { + public VariableDef() { } + + protected override TypedDependencyInfo NewDefinition(int version) { + return new TypedDependencyInfo(version); + } + + public bool AddTypes(Node node, AnalysisUnit unit, IEnumerable newTypes) { + bool added = false; + + foreach (var value in newTypes) { + var declaringModule = value.DeclaringModule; + if (declaringModule == null || declaringModule.Version == value.DeclaringVersion) { + var dependencies = GetDependentItems(declaringModule ?? unit.ProjectEntry); + + if (dependencies.Types.Add(value, unit.ProjectState)) { + added = true; + } + } + } + + if (added) { + EnqueueDependents(); + } + + return added; + } + + public ISet Types { + get { + if (_dependencies.Count != 0) { + HashSet res = new HashSet(); + foreach (var mod in _dependencies.Values) { + res.UnionWith(mod.Types); + } + return res; + } + return EmptySet.Instance; + } + } + + public void AddReference(Node node, AnalysisUnit unit) { + if (!unit.ForEval) { + AddReference(new SimpleSrcLocation(node.Span), unit.DeclaringModule.ProjectEntry).AddDependentUnit(unit); + } + } + + public TypedDependencyInfo AddReference(SimpleSrcLocation location, IProjectEntry module) { + var depUnits = GetDependentItems(module); + depUnits.References.Add(location); + return depUnits; + } + + public void AddAssignment(SimpleSrcLocation location, IProjectEntry entry) { + var depUnits = GetDependentItems(entry); + depUnits.Assignments.Add(location); + } + + public void AddAssignment(Node node, AnalysisUnit unit) { + if (!unit.ForEval) { + AddAssignment(new SimpleSrcLocation(node.Span), unit.DeclaringModule.ProjectEntry); + } + } + + public IEnumerable> References { + get { + if (_dependencies.Count != 0) { + foreach (var keyValue in _dependencies) { + foreach (var reference in keyValue.Value.References) { + yield return new KeyValuePair(keyValue.Key, reference); + } + } + } + } + } + + public IEnumerable> Definitions { + get { + if (_dependencies.Count != 0) { + foreach (var keyValue in _dependencies) { + foreach (var reference in keyValue.Value.Assignments) { + yield return new KeyValuePair(keyValue.Key, reference); + } + } + } + } + } + } + + /// + /// A variable def which has a specific location where it is defined (currently just function parameters). + /// + sealed class LocatedVariableDef : VariableDef { + private readonly IProjectEntry _entry; + private readonly Node _location; + + public LocatedVariableDef(IProjectEntry entry, Node location) { + _entry = entry; + _location = location; + } + + public IProjectEntry Entry { + get { + return _entry; + } + } + + public Node Node { + get { + return _location; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/LocationInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/LocationInfo.cs new file mode 100644 index 0000000000..2fae575d84 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/LocationInfo.cs @@ -0,0 +1,75 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.PyAnalysis { + public class LocationInfo : IEquatable { + private readonly int _line, _column, _length; + private readonly IProjectEntry _entry; + + internal LocationInfo(IProjectEntry entry, int line, int column, int length) { + _entry = entry; + _line = line; + _column = column; + _length = length; + } + + internal IProjectEntry ProjectEntry { + get { + return _entry; + } + } + + public string FilePath { + get { return _entry.FilePath; } + } + + public int Line { + get { return _line; } + } + + public int Column { + get { + return _column; + } + } + + public int Length { + get { + return _length; + } + } + + public override bool Equals(object obj) { + LocationInfo other = obj as LocationInfo; + if (other != null) { + return Equals(other); + } + return false; + } + + public override int GetHashCode() { + return Line.GetHashCode() ^ ProjectEntry.GetHashCode(); + } + + public bool Equals(LocationInfo other) { + // currently we filter only to line & file - so we'll only show 1 ref per each line + // This works nicely for get and call which can both add refs and when they're broken + // apart you still see both refs, but when they're together you only see 1. + return Line == other.Line && + ProjectEntry == other.ProjectEntry; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/MemberResult.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/MemberResult.cs new file mode 100644 index 0000000000..fefc629039 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/MemberResult.cs @@ -0,0 +1,92 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Text; +using Microsoft.PyAnalysis.Values; + +namespace Microsoft.PyAnalysis { + public struct MemberResult { + private readonly string _name; + private string _completion; + private readonly IEnumerable _vars; + private readonly ResultType? _type; + + internal MemberResult(string name, IEnumerable vars) { + _name = _completion = name; + _vars = vars; + _type = null; + } + + internal MemberResult(string name, string completion, IEnumerable vars) { + _name = name; + _vars = vars; + _completion = completion; + _type = null; + } + + + internal MemberResult(string name, ResultType type) { + _name = name; + _type = type; + _completion = _name; + _vars = Empty; + } + + private static Namespace[] Empty = new Namespace[0]; + + public string Name { + get { return _name; } + } + + public string Completion { + get { return _completion; } + } + + public ResultType MemberType { + get { + return _type ?? GetMemberType(); + } + } + + private ResultType GetMemberType() { + ResultType result = ResultType.Unknown; + foreach (var ns in _vars) { + var nsType = ns.ResultType; + if (result == ResultType.Unknown) { + result = nsType; + } else if (result != nsType) { + return ResultType.Multiple; + } + } + return result; + } + + internal IEnumerable Namespaces { + get { + return _vars; + } + } + + public string ToolTip { + get { + var doc = new StringBuilder(); + foreach (var ns in _vars) { + doc.Append(ns.Documentation); + } + return Utils.CleanDocumentation(doc.ToString()); + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ModuleAnalysis.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ModuleAnalysis.cs new file mode 100644 index 0000000000..04e9b291c7 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ModuleAnalysis.cs @@ -0,0 +1,483 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using IronPython.Compiler.Ast; +using IronPython.Runtime.Types; +using Microsoft.PyAnalysis.Interpreter; +using Microsoft.PyAnalysis.Values; +using Microsoft.Scripting; +using Microsoft.Scripting.Library; + +namespace Microsoft.PyAnalysis { + /// + /// Encapsulates all of the information about a single module which has been analyzed. + /// + /// Can be queried for various information about the resulting analysis. + /// + public sealed class ModuleAnalysis { + private readonly AnalysisUnit _unit; + private readonly InterpreterScope[] _scopes; + private readonly Stack _scopeTree; + + internal ModuleAnalysis(AnalysisUnit unit, Stack tree) { + _unit = unit; + _scopes = unit.Scopes; + _scopeTree = tree; + } + + #region Public API + + /// + /// Evaluates the given expression in at the provided line number and returns the values + /// that the expression can evaluate to. + /// + /// The expression to determine the result of. + /// The line number to evaluate at within the module. + public IEnumerable GetValues(string exprText, int lineNumber) { + var expr = GetExpressionFromText(exprText); + var scopes = FindScopes(lineNumber); + var eval = new ExpressionEvaluator(_unit.CopyForEval(), scopes.ToArray()); + + var res = eval.Evaluate(expr); + foreach (var v in res) { + yield return v; + } + } + + private IEnumerable ToVariables(IReferenceable referenceable) { + LocatedVariableDef locatedDef = referenceable as LocatedVariableDef; + if (locatedDef != null) { + yield return new AnalysisVariable(VariableType.Definition, new LocationInfo(locatedDef.Entry, locatedDef.Node.Start.Line, locatedDef.Node.Start.Column, locatedDef.Node.Span.Length)); + } + + foreach (var reference in referenceable.Definitions) { + yield return new AnalysisVariable(VariableType.Definition, new LocationInfo(reference.Key, reference.Value.Line, reference.Value.Column, reference.Value.Length)); + } + + foreach (var reference in referenceable.References) { + yield return new AnalysisVariable(VariableType.Reference, new LocationInfo(reference.Key, reference.Value.Line, reference.Value.Column, reference.Value.Length)); + } + } + + /// + /// Gets the variables the given expression evaluates to. Variables include parameters, locals, and fields assigned on classes, modules and instances. + /// + /// Variables are classified as either definitions or references. Only parameters have unique definition points - all other types of variables + /// have only one or more references. + /// + public IEnumerable GetVariables(string exprText, int lineNumber) { + var expr = GetExpressionFromText(exprText); + var scopes = FindScopes(lineNumber); + var eval = new ExpressionEvaluator(_unit.CopyForEval(), FindScopes(lineNumber).ToArray()); + NameExpression name = expr as NameExpression; + if (name != null) { + for (int i = scopes.Count - 1; i >= 0; i--) { + VariableDef def; + if (IncludeScope(scopes, i, lineNumber) && scopes[i].Variables.TryGetValue(name.Name, out def)) { + foreach (var res in ToVariables(def)) { + yield return res; + } + + if (scopes[i] is FunctionScope) { + // if this is a parameter or a local indicate any values which we know are assigned to it. + foreach (var type in def.Types) { + if (type.Location != null) { + yield return new AnalysisVariable(VariableType.Value, type.Location); + } + } + } else if (scopes[i] is ModuleScope) { + foreach (var type in def.Types) { + if (type.Location != null) { + yield return new AnalysisVariable(VariableType.Definition, type.Location); + } + + foreach (var reference in type.References) { + yield return new AnalysisVariable(VariableType.Reference, reference); + } + } + } + + } + } + + var variables = _unit.ProjectState.BuiltinModule.GetDefinitions(name.Name); + foreach (var referenceable in variables) { + foreach (var res in ToVariables(referenceable)) { + yield return res; + } + } + } + + MemberExpression member = expr as MemberExpression; + if (member != null) { + var objects = eval.Evaluate(member.Target); + + foreach (var v in objects) { + var container = v as IReferenceableContainer; + if (container != null) { + var defs = container.GetDefinitions(member.Name); + + foreach (var def in defs) { + foreach (var reference in def.Definitions) { + yield return new AnalysisVariable(VariableType.Definition, new LocationInfo(reference.Key, reference.Value.Line, reference.Value.Column, reference.Value.Length)); + } + + foreach (var reference in def.References) { + yield return new AnalysisVariable(VariableType.Reference, new LocationInfo(reference.Key, reference.Value.Line, reference.Value.Column, reference.Value.Length)); + } + } + } + } + } + } + + private static bool IncludeScope(List scopes, int i, int lineNo) { + if(scopes[i].VisibleToChildren || i == scopes.Count - 1) { + return true; + } + + // if we're on the 1st line of a function include our class def as well + if (i == scopes.Count - 2 && scopes[scopes.Count - 1] is FunctionScope) { + var funcScope = (FunctionScope)scopes[scopes.Count - 1]; + if (lineNo == funcScope.Function.FunctionDefinition.Start.Line) { + return true; + } + } + return false; + } + + /// + /// Evaluates a given expression and returns a list of members which exist in the expression. + /// + public IEnumerable GetMembers(string exprText, int lineNumber, bool intersectMultipleResults = true) { + if (exprText.EndsWith(".")) { + exprText = exprText.Substring(0, exprText.Length - 1); + if (exprText.Length == 0) { + // don't return all available members on empty dot. + return new MemberResult[0]; + } + } else { + int cut = exprText.LastIndexOfAny(new[] { '.', ']', ')' }); + if (cut != -1) { + exprText = exprText.Substring(0, cut); + } else { + exprText = String.Empty; + } + } + + if (exprText.Length == 0) { + return GetAllAvailableMembers(lineNumber); + } else { + var expr = GetExpressionFromText(exprText); + if (expr is ConstantExpression && ((ConstantExpression)expr).Value is int) { + // no completions on integer ., the user is typing a float + return new MemberResult[0]; + } + var lookup = GetVariablesForExpression(expr, lineNumber); + return GetMemberResults(lookup, intersectMultipleResults); + } + } + + + /// + /// Gets information about the available signatures for the given expression. + /// + /// The expression to get signatures for. + /// The line number to use for the context of looking up members. + public IEnumerable GetSignatures(string exprText, int lineNumber) { + try { + var eval = new ExpressionEvaluator(_unit.CopyForEval(), FindScopes(lineNumber).ToArray()); + var sourceUnit = ProjectState.GetSourceUnitForExpression(exprText); + using (var parser = Utils.CreateParser(sourceUnit, new CollectingErrorSink())) { + var expr = GetExpression(parser.ParseTopExpression().Body); + if (expr is ListExpression || + expr is TupleExpression || + expr is DictionaryExpression) { + return new OverloadResult[0]; + } + var lookup = eval.Evaluate(expr); + + var result = new List(); + + // TODO: Include relevant type info on the parameter... + foreach (var ns in lookup) { + result.AddRange(ns.Overloads); + } + + return result.ToArray(); + } + } catch (Exception) { + // TODO: log exception + return new[] { new SimpleOverloadResult(new ParameterResult[0], "Unknown", "IntellisenseError_Sigs") }; + } + } + + /// + /// Gets the available names at the given location. This includes built-in variables, global variables, and locals. + /// + /// The line number where the available mebmers should be looked up. + public IEnumerable GetAllAvailableMembers(int lineNumber) { + var result = new Dictionary>(); + + // collect builtins + foreach (var variable in ProjectState.BuiltinModule.VariableDict) { + result[variable.Key] = new List(variable.Value); + } + + // collect variables from user defined scopes + foreach (var scope in FindScopes(lineNumber)) { + foreach (var kvp in scope.Variables) { + result[kvp.Key] = new List(kvp.Value.Types); + } + } + return MemberDictToResultList(result); + } + + #endregion + + /// + /// TODO: This should go away, it's only used for tests. + /// + /// + /// + /// + internal IEnumerable GetTypesFromName(string name, int lineNumber) { + var chain = FindScopes(lineNumber); + var result = new HashSet(); + foreach (var scope in chain) { + if (scope.VisibleToChildren || scope == chain[chain.Count - 1]) { + VariableDef v; + if (scope.Variables.TryGetValue(name, out v)) { + foreach (var ns in v.Types) { + // add the clr type + // TODO: handle null? + if (ns != null && ns.PythonType != null) { + result.Add(ns.PythonType); + } + } + } + } + } + return result; + } + + /// + /// Returns a list of valid names available at the given position in the analyzed source code minus the builtin variables. + /// + /// TODO: This should go away, it's only used for tests. + /// + /// The line number where the available mebmers should be looked up. + /// + internal IEnumerable GetVariablesNoBuiltins(int lineNumber) { + var chain = FindScopes(lineNumber); + foreach (var scope in chain) { + if (scope.VisibleToChildren || scope == chain[chain.Count - 1]) { + foreach (var varName in scope.Variables) { + yield return varName.Key; + } + } + } + } + + /// + /// TODO: This method should go away, it's only being used for tests, and the tests should be using GetMembersFromExpression + /// which may need to be cleaned up. + /// + internal IEnumerable GetMembersFromName(string name, int lineNumber) { + var lookup = GetVariablesForExpression(GetExpressionFromText(name), lineNumber); + return GetMemberResults(lookup).Select(m => m.Name); + } + + /// + /// Gets the top-level scope for the module. + /// + internal ModuleInfo GlobalScope { + get { + var result = (Scopes[0] as ModuleScope); + Debug.Assert(result != null); + return result.Module; + } + } + + /// + /// Gets the tree of all scopes for the module. + /// + internal Stack ScopeTree { + get { return _scopeTree; } + } + + internal ProjectState ProjectState { + get { return GlobalScope.ProjectEntry.ProjectState; } + } + + internal InterpreterScope[] Scopes { + get { return _scopes; } + } + + internal IEnumerable GetMemberResults(IEnumerable vars, bool intersectMultipleResults = true) { + IList namespaces = new List(); + foreach (var ns in vars) { + if (ns != null) { + namespaces.Add(ns); + } + } + + if (namespaces.Count == 1) { + // optimize for the common case of only a single namespace + var newMembers = namespaces[0].GetAllMembers(GlobalScope.ShowClr); + if (newMembers == null || newMembers.Count == 0) { + return new MemberResult[0]; + } + + return SingleMemberResult(newMembers); + } + + Dictionary> memberDict = null; + HashSet memberSet = null; + foreach (Namespace ns in namespaces) { + if (ProjectState._noneInst == ns) { + continue; + } + + var newMembers = ns.GetAllMembers(GlobalScope.ShowClr); + // IntersectMembers(members, memberSet, memberDict); + if (newMembers == null || newMembers.Count == 0) { + continue; + } + + if (memberSet == null) { + // first namespace, add everything + memberSet = new HashSet(newMembers.Keys); + memberDict = new Dictionary>(); + foreach (var kvp in newMembers) { + var tmp = new List(kvp.Value); + memberDict[kvp.Key] = tmp; + } + } else { + // 2nd or nth namespace, union or intersect + HashSet toRemove; + IEnumerable adding; + + if (intersectMultipleResults) { + adding = new HashSet(newMembers.Keys); + // Find the things only in memberSet that we need to remove from memberDict + // toRemove = (memberSet ^ adding) & memberSet + + toRemove = new HashSet(memberSet); + toRemove.SymmetricExceptWith(adding); + toRemove.IntersectWith(memberSet); + + // intersect memberSet with what we're adding + memberSet.IntersectWith(adding); + + // we're only adding things they both had + adding = memberSet; + } else { + // we're adding all of newMembers keys + adding = newMembers.Keys; + toRemove = null; + } + + // update memberDict + foreach (var name in adding) { + List values; + if (!memberDict.TryGetValue(name, out values)) { + memberDict[name] = values = new List(); + } + values.AddRange(newMembers[name]); + } + + if (toRemove != null) { + foreach (var name in toRemove) { + memberDict.Remove(name); + } + } + } + } + + if (memberDict == null) { + return new MemberResult[0]; + } + return MemberDictToResultList(memberDict); + } + + private Expression GetExpressionFromText(string exprText) { + SourceUnit sourceUnit = ProjectState.GetSourceUnitForExpression(exprText); + using (var parser = Utils.CreateParser(sourceUnit, new CollectingErrorSink())) { + return GetExpression(parser.ParseTopExpression().Body); + } + } + + private ISet GetVariablesForExpression(Expression expr, int lineNumber) { + return new ExpressionEvaluator(_unit.CopyForEval(), FindScopes(lineNumber).ToArray()).Evaluate(expr); + } + + private static IEnumerable SingleMemberResult(IDictionary> memberDict) { + foreach (var kvp in memberDict) { + yield return new MemberResult(kvp.Key, kvp.Value); + } + } + + private Expression GetExpression(Statement statement) { + if (statement is ExpressionStatement) { + return ((ExpressionStatement)statement).Expression; + } else if (statement is ReturnStatement) { + return ((ReturnStatement)statement).Expression; + } else { + return null; + } + } + + /// + /// Gets the chain of scopes which are associated with the given position in the code. + /// + private List FindScopes(int lineNumber) { + ScopePositionInfo curScope = ScopeTree.First(); + ScopePositionInfo prevScope = null; + var chain = new List { Scopes[0] }; + + while (curScope != prevScope) { + prevScope = curScope; + + // TODO: Binary search? + // We currently search backwards because the end positions are sometimes unreliable + // and go onto the next line overlapping w/ the previous definition. Therefore searching backwards always + // hits the valid method first matching on Start. For example: + // def f(): # Starts on 1, ends on 3 + // pass + // def g(): # starts on 3, ends on 4 + // pass + for (int i = curScope.Children.Count - 1; i >= 0; i--) { + var scope = curScope.Children[i]; + if (scope.Start <= lineNumber && scope.Stop >= lineNumber) { + curScope = scope; + chain.Add(curScope.Scope); + break; + } + } + } + return chain; + } + + private static IEnumerable MemberDictToResultList(Dictionary> memberDict) { + foreach (var kvp in memberDict) { + yield return new MemberResult(kvp.Key, kvp.Value); + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ModuleReference.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ModuleReference.cs new file mode 100644 index 0000000000..470e2ffe6b --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ModuleReference.cs @@ -0,0 +1,35 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.PyAnalysis.Values; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis { + class ModuleReference { + public Namespace Module; + public HashSet References; + + public ModuleReference() { + } + + public ModuleReference(Namespace module) { + Module = module; + } + + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/OverloadResult.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/OverloadResult.cs new file mode 100644 index 0000000000..ad717b868e --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/OverloadResult.cs @@ -0,0 +1,222 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Reflection; +using System.Text; +using System.Threading; +using IronPython.Runtime; +using IronPython.Runtime.Operations; +using IronPython.Runtime.Types; +using Microsoft.Scripting; +using Microsoft.Scripting.Generation; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.PyAnalysis { + public class OverloadResult : IOverloadResult { + private readonly ParameterResult[] _parameters; + private readonly string _name; + + public OverloadResult(ParameterResult[] parameters, string name) { + _parameters = parameters; + _name = name; + } + + public string Name { + get { return _name; } + } + public virtual string Documentation { + get { return null; } + } + public virtual ParameterResult[] Parameters { + get { return _parameters; } + } + } + + public class SimpleOverloadResult : OverloadResult { + private readonly string _documentation; + public SimpleOverloadResult(ParameterResult[] parameters, string name, string documentation) + : base(parameters, name) { + _documentation = ParameterResult.Trim(documentation); + } + + public override string Documentation { + get { + return _documentation; + } + } + } + + public class BuiltinFunctionOverloadResult : OverloadResult { + private static readonly string _codeCtxType = "IronPython.Runtime.CodeContext"; + private readonly BuiltinFunction _overload; + private ParameterResult[] _parameters; + private readonly ParameterResult[] _extraParameters; + private readonly int _removedParams; + private readonly ProjectState _projectState; + private string _doc; + private static readonly string _calculating = "Documentation is still being calculated, please try again soon."; + + internal BuiltinFunctionOverloadResult(ProjectState state, BuiltinFunction overload, int removedParams, params ParameterResult[] extraParams) + : base(null, overload.__name__) { + _overload = overload; + _extraParameters = extraParams; + _removedParams = removedParams; + _projectState = state; + + CalculateDocumentation(); + } + + internal BuiltinFunctionOverloadResult(ProjectState state, BuiltinFunction overload, int removedParams, string name, params ParameterResult[] extraParams) + : base(null, name) { + _overload = overload; + _extraParameters = extraParams; + _removedParams = removedParams; + _projectState = state; + + CalculateDocumentation(); + } + + public override string Documentation { + get { + return _doc; + } + } + + private void CalculateDocumentation() { + // initially fill in w/ a string saying we don't yet have the documentation + _doc = _calculating; + + // and then asynchronously calculate the documentation + ThreadPool.QueueUserWorkItem( + x => { + var overloadDoc = _projectState.DocProvider.GetOverloads(_overload).First(); + StringBuilder doc = new StringBuilder(); + if (!String.IsNullOrEmpty(overloadDoc.Documentation)) { + doc.AppendLine(overloadDoc.Documentation); + } + + foreach (var param in overloadDoc.Parameters) { + if (!String.IsNullOrEmpty(param.Documentation)) { + doc.AppendLine(); + doc.Append(param.Name); + doc.Append(": "); + doc.Append(param.Documentation); + } + } + + if (!String.IsNullOrEmpty(overloadDoc.ReturnParameter.Documentation)) { + doc.AppendLine(); + doc.AppendLine(); + doc.Append("Returns: "); + doc.Append(overloadDoc.ReturnParameter.Documentation); + } + _doc = doc.ToString(); + } + ); + } + + public override ParameterResult[] Parameters { + get { + if (_parameters == null) { + if (_overload != null && _overload.Targets.Count > 0) { + Debug.Assert(_overload.Targets.Count == 1, "we should always get BuiltinFunctions via .Overloads.Functions which should only have 1 function each"); + var target = _overload.Targets[0]; + + bool isInstanceExtensionMethod = false; + if (!target.DeclaringType.IsAssignableFrom(_overload.DeclaringType)) { + // extension method + isInstanceExtensionMethod = !target.IsDefined(typeof(StaticExtensionMethodAttribute), false); + } + + var pinfo = _overload.Targets[0].GetParameters(); + var result = new List(pinfo.Length + _extraParameters.Length); + int ignored = 0; + ParameterResult kwDict = null; + foreach (var param in pinfo) { + if (result.Count == 0 && param.ParameterType.FullName == _codeCtxType) { + continue; + } else if (result.Count == 0 && isInstanceExtensionMethod) { + // skip instance parameter + isInstanceExtensionMethod = false; + continue; + } else if (ignored < _removedParams) { + ignored++; + } else { + var paramResult = GetParameterResultFromParameterInfo(param); + if (param.IsDefined(typeof(ParamDictionaryAttribute), false)) { + kwDict = paramResult; + } else { + result.Add(paramResult); + } + } + } + + result.InsertRange(0, _extraParameters); + + // always add kw dict last. When defined in C# and combined w/ params + // it has to come earlier than it's legally allowed in Python so we + // move it to the end for intellisense purposes here. + if (kwDict != null) { + result.Add(kwDict); + } + _parameters = result.ToArray(); + } else { + _parameters = new ParameterResult[0]; + } + } + return _parameters; + } + } + + internal static ParameterResult GetParameterResultFromParameterInfo(ParameterInfo param) { + // TODO: Get parameter documentation + var pyType = ClrModule.GetPythonType(param.ParameterType); + + string name = param.Name; + string typeName = PythonType.Get__name__(pyType); + if (param.IsDefined(typeof(ParamArrayAttribute), false)) { + name = "*" + name; + if (param.ParameterType.IsArray) { + var elemType = param.ParameterType.GetElementType(); + if (elemType == typeof(object)) { + typeName = "sequence"; + } else { + typeName = PythonType.Get__name__(DynamicHelpers.GetPythonTypeFromType(elemType)) + " sequence"; + } + } + } else if (param.IsDefined(typeof(ParamDictionaryAttribute), false)) { + name = "**" + name; + typeName = "object"; + } + + bool isOptional = false; + if (param.DefaultValue != DBNull.Value && !(param.DefaultValue is Missing)) { + name = name + " = " + PythonOps.Repr(DefaultContext.Default, param.DefaultValue); + } else if (param.IsOptional) { + object missing = CompilerHelpers.GetMissingValue(param.ParameterType); + if (missing != Missing.Value) { + name = name + " = " + PythonOps.Repr(DefaultContext.Default, missing); + } else { + isOptional = true; + } + } + + return new ParameterResult(name, "", typeName, isOptional); + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ProjectEntry.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ProjectEntry.cs new file mode 100644 index 0000000000..de2aa4fee0 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ProjectEntry.cs @@ -0,0 +1,302 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using IronPython.Compiler.Ast; +using Microsoft.IronPythonTools.Intellisense; +using Microsoft.IronStudio.Intellisense; +using Microsoft.PyAnalysis.Interpreter; +using Microsoft.PyAnalysis.Values; + +namespace Microsoft.PyAnalysis { + /// + /// Provides interactions to analysis a single file in a project and get the results back. + /// + /// To analyze a file the tree should be updated with a call to UpdateTree and then PreParse + /// should be called on all files. Finally Parse should then be called on all files. + /// + internal sealed class ProjectEntry : IPythonProjectEntry { + private readonly ProjectState _projectState; + private readonly string _moduleName; + private readonly string _filePath; + private IAnalysisCookie _cookie; + private ModuleInfo _myScope; + private PythonAst _tree; + private Stack _scopeTree; + private ModuleAnalysis _currentAnalysis; + private AnalysisUnit _unit; + private int _version; + + internal ProjectEntry(ProjectState state, string moduleName, string filePath, IAnalysisCookie cookie) { + Debug.Assert(moduleName != null); + Debug.Assert(filePath != null); + + _projectState = state; + _moduleName = moduleName ?? ""; + _filePath = filePath; + _cookie = cookie; + _myScope = new ModuleInfo(_moduleName, this); + _unit = new AnalysisUnit(_tree, new InterpreterScope[] { _myScope.Scope }, null); + } + + public event EventHandler OnNewParseTree; + public event EventHandler OnNewAnalysis; + + public void UpdateTree(PythonAst newAst, IAnalysisCookie newCookie) { + lock (this) { + _tree = newAst; + _cookie = newCookie; + } + + var newParse = OnNewParseTree; + if (newParse != null) { + newParse(this, EventArgs.Empty); + } + } + + public void GetTreeAndCookie(out PythonAst tree, out IAnalysisCookie cookie) { + lock (this) { + tree = _tree; + cookie = _cookie; + } + } + + public void Analyze() { + lock (this) { + _version++; + + Parse(); + + var newAnalysis = OnNewAnalysis; + if (newAnalysis != null) { + newAnalysis(this, EventArgs.Empty); + } + } + } + + public int Version { + get { + return _version; + } + } + + public bool IsAnalyzed { + get { + return Analysis != null; + } + } + + private void Parse() { + if (_tree == null) { + return; + } + + var oldParent = _myScope.ParentPackage; + ProjectState.ModulesByFilename[_filePath] = _myScope; + + if (oldParent != null) { + // update us in our parent package + _myScope.ParentPackage = oldParent; + oldParent.Scope.SetVariable(_tree, _unit, _moduleName.Substring(_moduleName.IndexOf('.') + 1), _myScope.SelfSet, false); + } + + var unit = _unit = new AnalysisUnit(_tree, new InterpreterScope[] { _myScope.Scope }, null); + + // collect top-level definitions first + var walker = new OverviewWalker(this, unit); + _tree.Walk(walker); + _scopeTree = walker.ScopeTree; + + PublishPackageChildrenInPackage(); + + // create new analysis object and analyze the code. + var newAnalysis = new ModuleAnalysis(_unit, _scopeTree); + _unit.Enqueue(); + + new DDG().Analyze(_projectState.Queue); + + // publish the analysis now that it's complete + _currentAnalysis = newAnalysis; + + foreach (var variableInfo in _myScope.Scope.Variables) { + variableInfo.Value.ClearOldValues(this); + } + } + + private void PublishPackageChildrenInPackage() { + if (_filePath.EndsWith("__init__.py")) { + string dir = Path.GetDirectoryName(_filePath); + if (Directory.Exists(dir)) { + foreach (var file in Directory.GetFiles(dir)) { + if (file.EndsWith("__init__.py")) { + continue; + } + + ModuleInfo childModule; + if (_projectState.ModulesByFilename.TryGetValue(file, out childModule)) { + _myScope.Scope.SetVariable(childModule.ProjectEntry.Tree, _unit, Path.GetFileNameWithoutExtension(file), childModule, false); + childModule.ParentPackage = _myScope; + } + } + + foreach (var packageDir in Directory.GetDirectories(dir)) { + string package = Path.Combine(packageDir, "__init__.py"); + ModuleInfo childPackage; + if (File.Exists(package) && _projectState.ModulesByFilename.TryGetValue(package, out childPackage)) { + _myScope.Scope.SetVariable(childPackage.ProjectEntry.Tree, _unit, Path.GetFileName(packageDir), childPackage, false); + childPackage.ParentPackage = _myScope; + } + } + } + } + } + + public string GetLine(int lineNo) { + return _cookie.GetLine(lineNo); + } + + public ModuleAnalysis Analysis { + get { return _currentAnalysis; } + } + + public string FilePath { + get { return _filePath; } + } + + public IAnalysisCookie Cookie { + get { return _cookie; } + } + + internal ProjectState ProjectState { + get { return _projectState; } + } + + public PythonAst Tree { + get { return _tree; } + } + + internal ModuleInfo MyScope { + get { return _myScope; } + } + } + + /// + /// Represents a file which is capable of being analyzed. Can be cast to other project entry types + /// for more functionality. See also IPythonProjectEntry and IXamlProjectEntry. + /// + public interface IProjectEntry { + bool IsAnalyzed { get; } + void Analyze(); + int Version { + get; + } + + string FilePath { get; } + string GetLine(int lineNo); + } + + public interface IPythonProjectEntry : IProjectEntry { + PythonAst Tree { + get; + } + + ModuleAnalysis Analysis { + get; + } + + event EventHandler OnNewParseTree; + event EventHandler OnNewAnalysis; + + void UpdateTree(PythonAst ast, IAnalysisCookie fileCookie); + void GetTreeAndCookie(out PythonAst ast, out IAnalysisCookie cookie); + } + + sealed class XamlProjectEntry : IXamlProjectEntry { + private XamlAnalysis _analysis; + private readonly string _filename; + private int _version; + private TextReader _content; + private IAnalysisCookie _cookie; + private readonly ProjectState _projectState; + private HashSet _dependencies = new HashSet(); + + public XamlProjectEntry(ProjectState projectState, string filename) { + _projectState = projectState; + _filename = filename; + } + + public void UpdateContent(TextReader content, IAnalysisCookie fileCookie) { + _content = content; + _cookie = fileCookie; + } + + public void AddDependency(AnalysisUnit unit) { + _dependencies.Add(unit.ProjectEntry); + } + + #region IProjectEntry Members + + public bool IsAnalyzed { + get { return _analysis != null; } + } + + public void Analyze() { + lock (this) { + if (_analysis == null) { + _analysis = new XamlAnalysis(_filename); + _cookie = new FileCookie(_filename); + } + _analysis = new XamlAnalysis(_content); + + _version++; + + // update any .py files which depend upon us. + foreach (var dep in _dependencies) { + dep.Analyze(); + } + } + } + + public string FilePath { get { return _filename; } } + + public int Version { + get { + return _version; + } + } + + public string GetLine(int lineNo) { + return _cookie.GetLine(lineNo); + } + + #endregion + + #region IXamlProjectEntry Members + + public XamlAnalysis Analysis { + get { return _analysis; } + } + + #endregion + } + + public interface IXamlProjectEntry : IProjectEntry { + XamlAnalysis Analysis { + get; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ProjectState.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ProjectState.cs new file mode 100644 index 0000000000..9458a8658c --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ProjectState.cs @@ -0,0 +1,731 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Threading; +using IronPython.Compiler.Ast; +using IronPython.Hosting; +using IronPython.Modules; +using IronPython.Runtime; +using IronPython.Runtime.Operations; +using IronPython.Runtime.Types; +using Microsoft.IronPythonTools; +using Microsoft.PyAnalysis.Interpreter; +using Microsoft.PyAnalysis.Values; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Hosting.Providers; +using Microsoft.Scripting.Library; +using Microsoft.Scripting.Math; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.PyAnalysis { + /// + /// Connects multiple source files together into one project state for combined analysis + /// + class ProjectState { + private readonly ScriptEngine _pythonEngine; + private readonly CodeContext _codeContext; + private readonly CodeContext _codeContextCls; + + private readonly List _projectEntries; + private readonly Dictionary _modules; + private readonly Dictionary _modulesByFilename; + private readonly Dictionary _xamlByFilename = new Dictionary(); + private readonly Dictionary _itemCache; + private readonly BuiltinModule _builtinModule; + private readonly List> _references; + internal readonly Namespace _propertyObj, _classmethodObj, _staticmethodObj, _typeObj, _intType, _rangeFunc, _frozensetType; + internal readonly HashSet _objectSet; + internal readonly Namespace _functionType; + internal readonly BuiltinClassInfo _dictType, _listType, _tupleType, _generatorType, _stringType, _boolType, _setType; + internal readonly ConstantInfo _noneInst; + private readonly Queue _queue; + private readonly DocumentationProvider _docProvider; + private HashSet _assemblyLoadList = new HashSet(); + + private static object _nullKey = new object(); + + public ProjectState(ScriptEngine pythonEngine) { + _pythonEngine = pythonEngine; + _projectEntries = new List(); + _modules = new Dictionary(); + _modulesByFilename = new Dictionary(StringComparer.OrdinalIgnoreCase); + _itemCache = new Dictionary(); + + var pythonContext = HostingHelpers.GetLanguageContext(_pythonEngine) as PythonContext; + _codeContextCls = new ModuleContext(new PythonDictionary(), pythonContext).GlobalContext; + _codeContextCls.ModuleContext.ShowCls = true; + + _codeContext = new ModuleContext( + new PythonDictionary(), + HostingHelpers.GetLanguageContext(_pythonEngine) as PythonContext + ).GlobalContext; + + InitializeBuiltinModules(); + + // TODO: Use reflection-only! + _references = new List>(); + AddAssembly(LoadAssemblyInfo(typeof(string).Assembly)); + AddAssembly(LoadAssemblyInfo(typeof(Debug).Assembly)); + + + // cached for quick checks to see if we're a call to clr.AddReference + SpecializeFunction("clr", "AddReference", (n, unit, args) => AddReference(n, null)); + SpecializeFunction("clr", "AddReferenceByPartialName", (n, unit, args) => AddReference(n, ClrModule.LoadAssemblyByPartialName)); + SpecializeFunction("clr", "AddReferenceByName", (n, unit, args) => AddReference(n, null)); + SpecializeFunction("clr", "AddReferenceToFile", (n, unit, args) => AddReference(n, (s) => ClrModule.LoadAssemblyFromFile(_codeContext, s))); + SpecializeFunction("clr", "AddReferenceToFileAndPath", (n, unit, args) => AddReference(n, (s) => ClrModule.LoadAssemblyFromFileWithPath(_codeContext, s))); + + try { + SpecializeFunction("wpf", "LoadComponent", LoadComponent); + } catch (KeyNotFoundException) { + // IronPython.Wpf.dll isn't available... + } + + SpecializeFunction("__builtin__", "range", (n, unit, args) => unit.DeclaringModule.GetOrMakeNodeVariable(n, (nn) => new RangeInfo(ClrModule.GetPythonType(typeof(List)), unit.ProjectState).SelfSet)); + SpecializeFunction("__builtin__", "min", ReturnUnionOfInputs); + SpecializeFunction("__builtin__", "max", ReturnUnionOfInputs); + + _builtinModule = (BuiltinModule)Modules["__builtin__"].Module; + _propertyObj = GetBuiltin("property"); + _classmethodObj = GetBuiltin("classmethod"); + _staticmethodObj = GetBuiltin("staticmethod"); + _typeObj = GetBuiltin("type"); + _intType = GetBuiltin("int"); + _stringType = (BuiltinClassInfo)GetBuiltin("str"); + + _objectSet = new HashSet(new[] { GetBuiltin("object") }); + + _setType = (BuiltinClassInfo)GetNamespaceFromObjects(TypeCache.Set); + _rangeFunc = GetBuiltin("range"); + _frozensetType = GetBuiltin("frozenset"); + _functionType = GetNamespaceFromObjects(TypeCache.Function); + _generatorType = (BuiltinClassInfo)GetNamespaceFromObjects(DynamicHelpers.GetPythonTypeFromType(typeof(PythonGenerator))); + _dictType = (BuiltinClassInfo)GetNamespaceFromObjects(TypeCache.Dict); + _boolType = (BuiltinClassInfo)GetNamespaceFromObjects(TypeCache.Boolean); + _noneInst = (ConstantInfo)GetNamespaceFromObjects(new object[] { null }); + _listType = (BuiltinClassInfo)GetNamespaceFromObjects(TypeCache.List); + _tupleType = (BuiltinClassInfo)GetNamespaceFromObjects(TypeCache.PythonTuple); + + _queue = new Queue(); + + _docProvider = CodeContext.LanguageContext.GetService(); + } + + /// + /// Adds a new module of code to the list of available modules and returns a ProjectEntry object. + /// + /// The name of the module; used to associate with imports + /// The path to the file on disk + /// An application-specific identifier for the module + /// + public ProjectEntry AddModule(string moduleName, string filePath, IAnalysisCookie cookie) { + var entry = new ProjectEntry(this, moduleName, filePath, cookie); + _projectEntries.Add(entry); + if (moduleName != null) { + Modules[moduleName] = new ModuleReference(entry.MyScope); + } + if (filePath != null) { + _modulesByFilename[filePath] = entry.MyScope; + } + return entry; + } + + public XamlProjectEntry AddXamlFile(string filePath) { + var entry = new XamlProjectEntry(this, filePath); + _xamlByFilename[filePath] = entry; + return entry; + } + + /// + /// Gets a top-level list of all the available modules as a list of MemberResults. + /// + /// + public MemberResult[] GetModules() { + var d = new Dictionary>(); + foreach (var keyValue in Modules) { + var modName = keyValue.Key; + var moduleRef = keyValue.Value; + + HashSet l; + if (!d.TryGetValue(modName, out l)) { + d[modName] = l = new HashSet(); + } + if (moduleRef != null && moduleRef.Module != null) { + // The REPL shows up here with value=None + l.Add(moduleRef.Module); + } + } + + foreach (var r in _references) { + foreach (string key in r.Value.GetMemberNames()) { + var value = PythonAssemblyOps.GetBoundMember(_codeContext, r.Key, key); + HashSet l2; + if (!d.TryGetValue(key, out l2)) { + d[key] = l2 = new HashSet(); + } + l2.Add(GetNamespaceFromObjects(value)); + } + } + + var result = new MemberResult[d.Count]; + int pos = 0; + foreach (var kvp in d) { + result[pos++] = new MemberResult(kvp.Key, kvp.Value); + } + return result; + } + + /// + /// returns the MemberResults associated with modules in the specified + /// list of names. The list of names is the path through the module, for example + /// ['System', 'Runtime'] + /// + /// + public MemberResult[] GetModuleMembers(string[] names) { + return GetModuleMembers(names, true); + } + + /// + /// returns the MemberResults associated with modules in the specified + /// list of names. The list of names is the path through the module, for example + /// ['System', 'Runtime'] + /// + /// + public MemberResult[] GetModuleMembers(string[] names, bool bottom) { + IDictionary> d = null; + var nses = GetReflectedNamespaces(names, bottom); + if (nses == null) { + return new MemberResult[0]; + } + var ns = GetNamespaceFromObjects(nses); + if (ns != null) { + d = ns.GetAllMembers(true); + } + + ModuleReference moduleRef; + if (Modules.TryGetValue(names[0], out moduleRef) && moduleRef.Module != null) { + var module = moduleRef.Module; + var newDict = new Dictionary>(); + if (d != null) { + Update(newDict, d); + } + + d = newDict; + + var mod = module.SelfSet; + if (bottom) { + for (int i = 1; i < names.Length; i++) { + var next = names[i]; + // import Foo.Bar as Baz, we need to get Bar + VariableDef def; + ISet newMod = EmptySet.Instance; + bool madeSet = false; + foreach (var modItem in mod) { + BuiltinModule builtinMod = modItem as BuiltinModule; + if (builtinMod != null) { + ISet builtinValues; + if (builtinMod.VariableDict.TryGetValue(next, out builtinValues)) { + newMod = newMod.Union(builtinValues, ref madeSet); + } + } else { + ModuleInfo userMod = modItem as ModuleInfo; + if (userMod != null && userMod.Scope.Variables.TryGetValue(next, out def)) { + newMod = newMod.Union(def.Types, ref madeSet); + } + } + } + + mod = newMod; + if (mod.Count == 0) { + break; + } + } + } + + foreach (var modItem in mod) { + Update(d, modItem.GetAllMembers(false)); + } + } + + MemberResult[] result; + if (d != null) { + result = new MemberResult[d.Count]; + int pos = 0; + foreach (var kvp in d) { + result[pos++] = new MemberResult(kvp.Key, kvp.Value); + } + } else { + result = new MemberResult[0]; + } + + return result; + } + + /// + /// Replaces a built-in function (specified by module name and function name) with a customized + /// delegate which provides specific behavior for handling when that function is called. + /// + /// Currently this just provides a hook when the function is called - it could be expanded + /// to providing the interpretation of when the function is called as well. + /// + private void SpecializeFunction(string moduleName, string name, Func[], ISet> dlg) { + var module = Modules[moduleName]; + + BuiltinModule builtin = module.Module as BuiltinModule; + Debug.Assert(builtin != null); + if (builtin != null) { + foreach (var v in builtin.VariableDict[name]) { + BuiltinFunctionInfo funcInfo = v as BuiltinFunctionInfo; + if (funcInfo != null) { + builtin.VariableDict[name] = new SpecializedBuiltinFunction(this, funcInfo.Function, dlg).SelfSet; + break; + } + } + } + } + + public List ProjectEntries { + get { return _projectEntries; } + } + + internal Queue Queue { + get { + return _queue; + } + } + + private void InitializeBuiltinModules() { + string installDir = PythonRuntimeHost.GetPythonInstallDir(); + if (installDir != null) { + var dllDir = Path.Combine(installDir, "DLLs"); + if (Directory.Exists(dllDir)) { + foreach (var assm in Directory.GetFiles(dllDir)) { + try { + _pythonEngine.Runtime.LoadAssembly(Assembly.LoadFile(Path.Combine(dllDir, assm))); + } catch { + } + } + } + } + + var names = _pythonEngine.Operations.GetMember(_pythonEngine.GetSysModule(), "builtin_module_names"); + foreach (string modName in names) { + PythonModule mod = Importer.Import(_codeContextCls, modName, PythonOps.EmptyTuple, 0) as PythonModule; + Debug.Assert(mod != null); + + Modules[modName] = new ModuleReference(new BuiltinModule(mod, this, false)); + } + } + + private KeyValuePair LoadAssemblyInfo(Assembly assm) { + var nsTracker = new TopNamespaceTracker(_codeContext.LanguageContext.DomainManager); + nsTracker.LoadAssembly(assm); + return new KeyValuePair(assm, nsTracker); + } + + private void AddAssembly(KeyValuePair assembly) { + _references.Add(assembly); + ThreadPool.QueueUserWorkItem(state => { + // start initializing assemblies on background thread for quick response. + foreach (string key in assembly.Value.GetMemberNames()) { + var value = PythonAssemblyOps.GetBoundMember(_codeContext, assembly.Key, key); + } + }); + } + + private ISet ReturnUnionOfInputs(CallExpression call, AnalysisUnit unit, ISet[] args) { + ISet res = EmptySet.Instance; + bool madeSet = false; + foreach (var set in args) { + res = res.Union(set, ref madeSet); + } + return res; + } + + private ISet LoadComponent(CallExpression node, AnalysisUnit unit, ISet[]args) { + if (args.Length == 2) { + var xaml = args[1]; + var self = args[0]; + + foreach (var arg in xaml) { + string strConst = arg.GetConstantValue() as string; + if (strConst != null) { + // process xaml file, add attributes to self + string xamlPath = Path.Combine(Path.GetDirectoryName(unit.DeclaringModule.ProjectEntry.FilePath), strConst); + XamlProjectEntry xamlProject; + if (_xamlByFilename.TryGetValue(xamlPath, out xamlProject)) { + // TODO: Get existing analysis if it hasn't changed. + var analysis = xamlProject.Analysis; + + if (analysis == null) { + xamlProject.Analyze(); + analysis = xamlProject.Analysis; + } + + xamlProject.AddDependency(unit); + + var evalUnit = unit.CopyForEval(); + + // add named objects to instance + foreach (var keyValue in analysis.NamedObjects) { + var type = keyValue.Value; + if (type.Type.UnderlyingType != null) { + var ns = GetNamespaceFromObjects(DynamicHelpers.GetPythonTypeFromType(type.Type.UnderlyingType)); + if (ns is BuiltinClassInfo) { + ns = ((BuiltinClassInfo)ns).Instance; + } + self.SetMember(node, evalUnit, keyValue.Key, ns.SelfSet); + } + + // TODO: Better would be if SetMember took something other than a node, then we'd + // track references w/o this extra effort. + foreach (var inst in self) { + InstanceInfo instInfo = inst as InstanceInfo; + if (instInfo != null) { + VariableDef def; + if (instInfo.InstanceAttributes.TryGetValue(keyValue.Key, out def)) { + def.AddAssignment( + new SimpleSrcLocation(type.LineNumber, type.LineOffset, keyValue.Key.Length), + xamlProject + ); + } + } + } + } + + // add references to event handlers + foreach (var keyValue in analysis.EventHandlers) { + // add reference to methods... + var member = keyValue.Value; + + // TODO: Better would be if SetMember took something other than a node, then we'd + // track references w/o this extra effort. + foreach (var inst in self) { + InstanceInfo instInfo = inst as InstanceInfo; + if (instInfo != null) { + ClassInfo ci = instInfo.ClassInfo; + + VariableDef def; + if (ci.Scope.Variables.TryGetValue(keyValue.Key, out def)) { + def.AddReference( + new SimpleSrcLocation(member.LineNumber, member.LineOffset, keyValue.Key.Length), + xamlProject + ); + } + } + } + } + } + } + } + + // load component returns self + return self; + } + + return EmptySet.Instance; + } + + private ISet AddReference(CallExpression node, Func partialLoader) { + // processes a call to clr.AddReference updating project state + // so that it contains the newly loaded assembly. + foreach (var arg in node.Args) { + var cexpr = arg.Expression as ConstantExpression; + if (cexpr == null || !(cexpr.Value is string)) { + // can't process this add reference + continue; + } + + // TODO: Should we do a .NET reflection only load rather than + // relying on the CLR module here? That would prevent any code from + // running although at least we don't taint our own modules which + // are loaded with this current code. + var asmName = cexpr.Value as string; + if (asmName != null && _assemblyLoadList.Add(asmName)) { + Assembly asm = null; + try { + if (partialLoader != null) { + asm = partialLoader(asmName); + } else { + try { + asm = ClrModule.LoadAssemblyByName(_codeContext, asmName); + } catch { + asm = ClrModule.LoadAssemblyByPartialName(asmName); + } + } + } catch { + } + AddAssembly(asm); + } + } + return null; + } + + public void AddAssembly(Assembly asm) { + if (asm != null && _references.FindIndex(reference => (reference.Key == asm)) == -1) { + // TODO: When do assembly references get removed? + var nsTracker = new TopNamespaceTracker(_codeContext.LanguageContext.DomainManager); + nsTracker.LoadAssembly(asm); + AddAssembly(new KeyValuePair(asm, nsTracker)); + } + } + + /// + /// Gets a builtin value + /// + /// + /// + internal Namespace GetBuiltin(string name) { + return _builtinModule.VariableDict[name].First(); + } + + internal T GetCached(object key, Func maker) where T : class { + object result; + if (!_itemCache.TryGetValue(key, out result)) { + _itemCache[key] = result = maker(); + } else { + Debug.Assert(result is T); + } + return (result as T); + } + + internal object[] GetReflectedNamespaces(IList names, bool bottom) { + if (names == null || names.Count == 0) { + return null; + } + var topName = names[0]; + + if (String.IsNullOrEmpty(topName)) { + // user typed "import." + return null; + } + + var builtinRefs = new List(); + foreach (var asm in _references) { + object attr = NamespaceTrackerOps.GetCustomMember(_codeContext, asm.Value, topName); + if (attr != null && attr != OperationFailed.Value) { + if (bottom) { + for (int i = 1; i < names.Count; i++) { + if (names[i] != null) { + object nextAttr; + if (!TryGetMember(attr, names[i], out nextAttr) || nextAttr == null) { + attr = null; + break; + } + attr = nextAttr; + } + } + } + if (attr != null) { + builtinRefs.Add(attr); + } + } + } + + return builtinRefs.ToArray(); + } + + internal BuiltinModule BuiltinModule { + get { return _builtinModule; } + } + + internal T GetMember(object obj, string name) { + return (T)Builtin.getattr(_codeContext, obj, name); + } + + private bool TryGetMember(CodeContext codeContext, object obj, string name, out object value) { + NamespaceTracker nt = obj as NamespaceTracker; + if (nt != null) { + value = NamespaceTrackerOps.GetCustomMember(codeContext, nt, name); + return value != OperationFailed.Value; + } + + object result = Builtin.getattr(codeContext, obj, name, this); + if (result == this) { + value = null; + return false; + } else { + value = result; + return true; + } + } + + internal bool TryGetMember(object obj, string name, out object value) { + return TryGetMember(_codeContext, obj, name, out value); + } + + internal bool TryGetMember(object obj, string name, bool showClr, out object value) { + var cctx = showClr ? _codeContextCls : _codeContext; + return TryGetMember(cctx, obj, name, out value); + } + + internal SourceUnit GetSourceUnitForExpression(string expr) { + return StringTextContentProvider.Make(_pythonEngine, expr, SourceCodeKind.Expression); + } + + internal CodeContext CodeContext { + get { return _codeContext; } + } + + internal CodeContext CodeContextCls { + get { return _codeContextCls; } + } + + internal DocumentationProvider DocProvider { + get { return _docProvider; } + } + + internal BuiltinInstanceInfo GetInstance(Type type) { + return GetInstance(ClrModule.GetPythonType(type)); + } + + internal BuiltinInstanceInfo GetInstance(PythonType type) { + return GetBuiltinType(type).Instance; + } + + internal BuiltinClassInfo GetBuiltinType(Type type) { + return GetBuiltinType(ClrModule.GetPythonType(type)); + } + + internal BuiltinClassInfo GetBuiltinType(PythonType type) { + return GetCached(type, () => new BuiltinClassInfo(type, this)); + } + + internal Namespace GetNamespaceFromObjects(params object[] attrs) { + return GetNamespaceFromObjects((IEnumerable)attrs); + } + + internal Namespace GetNamespaceFromObjects(IEnumerable attrs) { + List values = new List(); + foreach (var attr in attrs) { + var attrType = (attr != null) ? attr.GetType() : typeof(DynamicNull); + if (attr is PythonType) { + values.Add(GetBuiltinType((PythonType)attr)); + } else if (attr is BuiltinFunction) { + var bf = (BuiltinFunction)attr; + if (bf.__self__ == null) { + values.Add(GetCached(attr, () => new BuiltinFunctionInfo(bf, this))); + } else { + values.Add(new BuiltinFunctionInfo(bf, this)); + } + } else if (attrType == typeof(BuiltinMethodDescriptor)) { + values.Add(GetCached(attr, () => new BuiltinMethodInfo((BuiltinMethodDescriptor)attr, this))); + } else if (attrType.IsEnum) { + values.Add(GetCached(attr, () => new EnumInstanceInfo(attr, this))); + } else if (attrType == typeof(ReflectedProperty) || attrType == typeof(ReflectedExtensionProperty)) { + values.Add(GetCached(attr, () => new BuiltinPropertyInfo((ReflectedGetterSetter)attr, this))); + } else if (attrType == typeof(ReflectedField)) { + values.Add(GetCached(attr, () => new BuiltinFieldInfo((ReflectedField)attr, this))); + } else if (attrType == typeof(ReflectedEvent)) { + values.Add(GetCached(attr, () => new BuiltinEventInfo((ReflectedEvent)attr, this))); + } else if (attrType == typeof(bool) || attrType == typeof(int) || attrType == typeof(Complex64) + || attrType == typeof(string) || attrType == typeof(long) || attrType == typeof(double) || + attr == null) { + var varRef = GetConstant(attr); + foreach (var constant in varRef) { + values.Add(constant); + } + } else if (attr is NamespaceTracker || attr is TypeGroup) { // TODO: Need to do better for TypeGroup's. + values.Add(GetCached(attr, () => new ReflectedNamespace(new[] { attr }, this))); + } else { + var pyAattrType = DynamicHelpers.GetPythonType(attr); + values.Add(GetCached(pyAattrType, () => new BuiltinClassInfo(pyAattrType, this)).Instance); + } + } + + if (values.Count == 1) { + return values[0]; + } else if (values.Count > 1) { + return GetCached(new NamespaceKey(values.ToArray()), () => new ReflectedNamespace(attrs, this)); + } else { + return null; + } + } + + class NamespaceKey : IEquatable { + private readonly Namespace[] _values; + + public NamespaceKey(Namespace[] values) { + _values = values; + } + + public override bool Equals(object obj) { + if (!(obj is NamespaceKey)) { + return false; + } + + return Equals((NamespaceKey)obj); + } + + public override int GetHashCode() { + int res = 0; + foreach (Namespace n in _values) { + res ^= n.GetHashCode(); + } + return res; + } + + #region IEquatable Members + + public bool Equals(NamespaceKey other) { + if (other._values.Length != _values.Length) { + return false; + } + + for (int i = 0; i < _values.Length; i++) { + if (_values[i] != other._values[i]) { + return false; + } + } + + return true; + } + + #endregion + } + + internal Dictionary Modules { + get { return _modules; } + } + + internal Dictionary ModulesByFilename { + get { return _modulesByFilename; } + } + + internal ISet GetConstant(object value) { + object key = value ?? _nullKey; + return GetCached>(key, () => new ConstantInfo(value, this).SelfSet); + } + + internal BuiltinClassInfo MakeGenericType(Type clrType, params Type[] clrIndexTypes) { + var genType = clrType.MakeGenericType(clrIndexTypes); + var pyType = ClrModule.GetPythonType(genType); + return GetCached(pyType, () => new BuiltinClassInfo(pyType, this)); + } + + private static void Update(IDictionary dict, IDictionary newValues) { + foreach (var kvp in newValues) { + dict[kvp.Key] = kvp.Value; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ResultType.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ResultType.cs new file mode 100644 index 0000000000..24cd834025 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/ResultType.cs @@ -0,0 +1,93 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.PyAnalysis { + /// + /// Indicates the type of a variable result lookup. + /// + /// Types can indicate a physical 1st class object (function, module, etc...) or they can + /// represent a logic piece of storage (field) + /// + public enum ResultType { + Unknown, + /// + /// The result is a user defined or built-in class. + /// + Class, + /// + /// An instance of a user defined or built-in class. + /// + Instance, + /// + /// The result is a delegate type. + /// + Delegate, + /// + /// The result is an instance of a delegate. + /// + DelegateInstance, + /// + /// The result is an enum type. + /// + Enum, + /// + /// The result is an enum instance. + /// + EnumInstance, + /// + /// An instance of a user defined or built-in function. + /// + Function, + /// + /// An instance of a user defined or built-in method. + /// + Method, + /// + /// An instance of a built-in or user defined module. + /// + Module, + /// + /// An instance of a namespace object that was imported from .NET. + /// + Namespace, + + /// + /// A constant defined in source code. + /// + Constant, + + /// + /// A .NET event object that is exposed to Python. + /// + Event, + /// + /// A .NET field object that is exposed to Python. + /// + Field, + /// + /// A .NET property object that is exposed to Python. + /// + Property, + + /// + /// A merge of multiple types. + /// + Multiple + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/SimpleSrcLocation.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/SimpleSrcLocation.cs new file mode 100644 index 0000000000..a691e2df9b --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/SimpleSrcLocation.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.PyAnalysis { + /// + /// Simple structure used to track a position in code w/ line and column info. + /// + struct SimpleSrcLocation : IEquatable { + public readonly int Line, Column, Length; + + public SimpleSrcLocation(int line, int column, int length) { + Line = line; + Column = column; + Length = length; + } + + public SimpleSrcLocation(Scripting.SourceSpan sourceSpan) { + Line = sourceSpan.Start.Line; + Column = sourceSpan.Start.Column; + Length = sourceSpan.Length; + } + + public override int GetHashCode() { + return Line ^ Column ^ Length; + } + + public override bool Equals(object obj) { + if (obj is SimpleSrcLocation) { + return Equals((SimpleSrcLocation)obj); + } + return false; + } + + #region IEquatable Members + + public bool Equals(SimpleSrcLocation other) { + return Line == other.Line && Column == other.Column && Length == other.Length; + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BoundBuiltinMethodInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BoundBuiltinMethodInfo.cs new file mode 100644 index 0000000000..9ea0607548 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BoundBuiltinMethodInfo.cs @@ -0,0 +1,77 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Interpreter; +using Microsoft.Scripting; + +namespace Microsoft.PyAnalysis.Values { + class BoundBuiltinMethodInfo : Namespace { + private readonly BuiltinMethodInfo _method; + private OverloadResult[] _overloads; + + public BoundBuiltinMethodInfo(BuiltinMethodInfo method) { + _method = method; + } + + public override bool IsBuiltin { + get { + return true; + } + } + + public override ResultType ResultType { + get { + return ResultType.Method; + } + } + + public override string Documentation { + get { + return _method.Documentation; + } + } + + public ProjectState ProjectState { + get { + return _method.ProjectState; + } + } + + public override string Description { + get { + return "bound built-in method " + _method.Name; + } + } + + public override ISet Call(Node node, AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + return _method.ReturnTypes; + } + + public override ICollection Overloads { + get { + if (_overloads == null) { + var overloads = _method.BuiltinFunctions; + var result = new OverloadResult[overloads.Length]; + for (int i = 0; i < result.Length; i++) { + result[i] = new BuiltinFunctionOverloadResult(_method.ProjectState, overloads[i], 0); + } + _overloads = result; + } + return _overloads; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinClassInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinClassInfo.cs new file mode 100644 index 0000000000..253a9b4797 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinClassInfo.cs @@ -0,0 +1,311 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Reflection; +using IronPython.Compiler.Ast; +using IronPython.Runtime; +using IronPython.Runtime.Types; +using Microsoft.PyAnalysis.Interpreter; +using Microsoft.Scripting.Utils; + +namespace Microsoft.PyAnalysis.Values { + internal class BuiltinClassInfo : BuiltinNamespace, IReferenceableContainer { + private BuiltinInstanceInfo _inst; + private string _doc; + private readonly MemberReferences _referencedMembers = new MemberReferences(); + private ReferenceDict _references; + + public BuiltinClassInfo(PythonType classObj, ProjectState projectState) + : base(new LazyDotNetDict(classObj, projectState, true)) { + // TODO: Get parameters from ctor + // TODO: All types should be shared via projectState + _type = classObj; + _doc = null; + } + + public override ISet Call(Node node, AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + // TODO: More Type propagation + + if (args.Length == 1 && typeof(Delegate).IsAssignableFrom(_type.__clrtype__())) { + var invokeArgs = BuiltinEventInfo.GetEventInvokeArgs(ProjectState, _type.__clrtype__()); + foreach (var arg in args) { + arg.Call(node, unit, invokeArgs, ArrayUtils.EmptyStrings); + } + } + return Instance.SelfSet; + } + + public override bool IsBuiltin { + get { + return true; + } + } + + public BuiltinInstanceInfo Instance { + get { + return _inst ?? (_inst = MakeInstance()); + } + } + + private BuiltinInstanceInfo MakeInstance() { + if (_type == TypeCache.Int32 || _type == TypeCache.BigInteger || _type == TypeCache.Double || _type == TypeCache.Complex) { + return new NumericInstanceInfo(this); + } + + return new BuiltinInstanceInfo(this); + } + + /// + /// Returns the overloads avaialble for calling the constructor of the type. + /// + public override ICollection Overloads { + get { + // TODO: sometimes might have a specialized __init__. + // This just covers typical .NET types + var clrType = ClrModule.GetClrType(_type); + if (!IsPythonType) { + var newMethods = clrType.GetMember("__new__", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Static); + var initMethods = clrType.GetMember("__init__", BindingFlags.InvokeMethod | BindingFlags.Public | BindingFlags.Instance); + if (newMethods.Length == 0 && initMethods.Length == 0) { + return GetClrOverloads(); + } + } else if (clrType == typeof(object)) { + return GetClrOverloads(); + } + + object ctor; + if (!ProjectState.TryGetMember(_type, "__new__", out ctor)) { + ctor = null; + } + var func = ctor as BuiltinFunction; + if (func == null) { + return new OverloadResult[0]; + } + + var result = new OverloadResult[func.Overloads.Functions.Count]; + for (int i = 0; i < result.Length; i++) { + var bf = func.Overloads.Functions[i] as BuiltinFunction; + result[i] = new BuiltinFunctionOverloadResult(ProjectState, bf, 1, PythonType.Get__name__(_type)); + } + return result; + } + } + + /// + /// Returns the overloads for a normal .NET type + /// + private OverloadResult[] GetClrOverloads() { + Type clrType = ClrModule.GetClrType(_type); + // just a normal .NET type... + var ctors = clrType.GetConstructors(BindingFlags.Public | BindingFlags.CreateInstance | BindingFlags.Instance); + + var overloads = new OverloadResult[ctors.Length]; + for (int i = 0; i < ctors.Length; i++) { + // TODO: Docs, python type name + var parameters = ctors[i].GetParameters(); + bool hasContext = parameters.Length > 0 && parameters[0].ParameterType == typeof(CodeContext); + + var paramResult = new ParameterResult[hasContext ? parameters.Length - 1 : parameters.Length]; + + for (int j = 0; j < paramResult.Length; j++) { + var curParam = parameters[j + (hasContext ? 1 : 0)]; + // TODO: Docs + paramResult[j] = BuiltinFunctionOverloadResult.GetParameterResultFromParameterInfo(curParam); + } + overloads[i] = new SimpleOverloadResult(paramResult, PythonType.Get__name__(_type), ""); + } + + return overloads; + } + + public override ISet GetMember(Node node, AnalysisUnit unit, string name) { + var res = base.GetMember(node, unit, name); + if (res.Count > 0) { + _referencedMembers.AddReference(node, unit, name); + return res.GetStaticDescriptor(unit); + } + return res; + } + + public override void SetMember(Node node, AnalysisUnit unit, string name, ISet value) { + var res = base.GetMember(node, unit, name); + if (res.Count > 0) { + _referencedMembers.AddReference(node, unit, name); + } + } + + public override ISet GetIndex(Node node, AnalysisUnit unit, ISet index) { + // TODO: Needs to actually do indexing on type + var clrType = _type.__clrtype__(); + if (!clrType.IsGenericTypeDefinition) { + return EmptySet.Instance; + } + + var result = new HashSet(); + foreach (var indexType in index) { + if (indexType is BuiltinClassInfo) { + var clrIndexType = indexType.PythonType.__clrtype__(); + try { + var klass = ProjectState.MakeGenericType(clrType, clrIndexType); + result.Add(klass); + } catch { + // wrong number of type args, violated constraint, etc... + } + } else if (indexType is SequenceInfo) { + List[] types = GetSequenceTypes(indexType as SequenceInfo); + + if (!MissingType(types)) { + foreach (Type[] indexTypes in GetTypeCombinations(types)) { + try { + var klass = ProjectState.MakeGenericType(clrType, indexTypes); + result.Add(klass); + } catch { + // wrong number of type args, violated constraint, etc... + } + } + } + } + } + return result; + } + + private static IEnumerable GetTypeCombinations(List[] types) { + List res = new List(); + for (int i = 0; i < types.Length; i++) { + res.Add(null); + } + + return GetTypeCombinationsWorker(types, res, 0); + } + + private static IEnumerable GetTypeCombinationsWorker(List[] types, List res, int curIndex) { + if (curIndex == types.Length) { + yield return res.ToArray(); + } else { + foreach (Type t in types[curIndex]) { + res[curIndex] = t; + + foreach (var finalRes in GetTypeCombinationsWorker(types, res, curIndex + 1)) { + yield return finalRes; + } + } + } + } + + private static List[] GetSequenceTypes(SequenceInfo seq) { + List[] types = new List[seq.IndexTypes.Length]; + for (int i = 0; i < types.Length; i++) { + foreach (var seqIndexType in seq.IndexTypes[i]) { + if (seqIndexType is BuiltinClassInfo) { + if (types[i] == null) { + types[i] = new List(); + } + + types[i].Add(seqIndexType.PythonType.__clrtype__()); + } + } + } + return types; + } + + private static bool MissingType(List[] types) { + for (int i = 0; i < types.Length; i++) { + if (types[i] == null) { + return true; + } + } + return false; + } + + public override string Description { + get { + return "type " + PythonType.Get__name__(_type); + } + } + + public override string Documentation { + get { + if (_doc == null) { + try { + var doc = PythonType.Get__doc__(ProjectState.CodeContext, _type); + _doc = Utils.StripDocumentation(doc.ToString()); + } catch { + _doc = String.Empty; + } + } + return _doc; + } + } + + public override ResultType ResultType { + get { + var type = _type.__clrtype__(); + if (type.IsEnum) { + return ResultType.Enum; + } else if (typeof(Delegate).IsAssignableFrom(type)) { + return ResultType.Delegate; + } else { + return ResultType.Class; + } + } + } + + public override string ToString() { + // return 'Class#' + hex(id(self)) + ' ' + self.clrType.__name__ + return "Class " + PythonType.Get__name__(_type); + } + + public bool IsPythonType { + get { + return _type == TypeCache.String || + _type == TypeCache.Object || + _type == TypeCache.Double || + _type == TypeCache.Complex || + _type == TypeCache.Boolean; + } + } + + #region IReferenceableContainer Members + + public IEnumerable GetDefinitions(string name) { + return _referencedMembers.GetDefinitions(name); + } + + #endregion + + internal void AddMemberReference(Node node, AnalysisUnit unit, string name) { + _referencedMembers.AddReference(node, unit, name); + } + + internal override void AddReference(Node node, AnalysisUnit unit) { + if (!unit.ForEval) { + if (_references == null) { + _references = new ReferenceDict(); + } + _references.GetReferences(unit.DeclaringModule.ProjectEntry).References.Add(new SimpleSrcLocation(node.Span)); + } + } + + public override IEnumerable References { + get { + if (_references != null) { + return _references.AllReferences; + } + return new LocationInfo[0]; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinEventInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinEventInfo.cs new file mode 100644 index 0000000000..35c7df2793 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinEventInfo.cs @@ -0,0 +1,74 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using IronPython.Runtime; +using IronPython.Runtime.Types; +using Microsoft.PyAnalysis.Interpreter; +using Microsoft.Scripting.Utils; + +namespace Microsoft.PyAnalysis.Values { + internal class BuiltinEventInfo : BuiltinNamespace { + private readonly ReflectedEvent _value; + private string _doc; + + public BuiltinEventInfo(ReflectedEvent value, ProjectState projectState) + : base(ClrModule.GetPythonType(value.Info.EventHandlerType), projectState) { + _value = value; + _doc = null; + _type = ClrModule.GetPythonType(value.Info.EventHandlerType); + } + + public override void AugmentAssign(AugmentedAssignStatement node, AnalysisUnit unit, ISet value) { + base.AugmentAssign(node, unit, value); + var args = GetEventInvokeArgs(ProjectState, _type); + foreach (var r in value) { + r.Call(node, unit, args, ArrayUtils.EmptyStrings); + } + } + + internal static ISet[] GetEventInvokeArgs(ProjectState state, Type type) { + var p = type.GetMethod("Invoke").GetParameters(); + + var args = new ISet[p.Length]; + for (int i = 0; i < p.Length; i++) { + args[i] = state.GetInstance(p[i].ParameterType).SelfSet; + } + return args; + } + + public override string Description { + get { + return "event of type " + _value.Info.EventHandlerType.ToString(); + } + } + + public override ResultType ResultType { + get { + return ResultType.Event; + } + } + + public override string Documentation { + get { + if (_doc == null) { + _doc = Utils.StripDocumentation(_value.__doc__); + } + return _doc; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinFieldInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinFieldInfo.cs new file mode 100644 index 0000000000..d6f1ce0c31 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinFieldInfo.cs @@ -0,0 +1,72 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronPython.Runtime; +using IronPython.Runtime.Types; + +namespace Microsoft.PyAnalysis.Values { + internal class BuiltinFieldInfo : BuiltinNamespace { + private readonly ReflectedField _value; + private string _doc; + + public BuiltinFieldInfo(ReflectedField value, ProjectState projectState) + : base(new LazyDotNetDict(ClrModule.GetPythonType(value.FieldType), projectState, true)) { + _value = value; + _doc = null; + _type = ClrModule.GetPythonType(value.FieldType); + } + + public override ISet GetDescriptor(Namespace instance, Interpreter.AnalysisUnit unit) { + BuiltinClassInfo klass = (BuiltinClassInfo)ProjectState.GetNamespaceFromObjects(_value.FieldType); + return klass.Instance.SelfSet; + } + + public override ISet GetStaticDescriptor(Interpreter.AnalysisUnit unit) { + if (_value.Info.IsStatic) { + BuiltinClassInfo klass = (BuiltinClassInfo)ProjectState.GetNamespaceFromObjects(_value.FieldType); + return klass.Instance.SelfSet; + } + + return base.GetStaticDescriptor(unit); + } + + public override string Description { + get { + return "field of type " + PythonType.Get__name__(_value.FieldType); + } + } + + public override bool IsBuiltin { + get { + return true; + } + } + + public override ResultType ResultType { + get { + return ResultType.Field; + } + } + + public override string Documentation { + get { + if (_doc == null) { + _doc = Utils.StripDocumentation(_value.__doc__); + } + return _doc; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinFunctionInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinFunctionInfo.cs new file mode 100644 index 0000000000..2d3c57435f --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinFunctionInfo.cs @@ -0,0 +1,89 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Collections.ObjectModel; +using IronPython.Compiler.Ast; +using IronPython.Runtime; +using IronPython.Runtime.Types; + +namespace Microsoft.PyAnalysis.Values { + internal class BuiltinFunctionInfo : BuiltinNamespace { + private BuiltinFunction _function; + private string _doc; + private ReadOnlyCollection _overloads; + private readonly ISet _returnTypes; + + public BuiltinFunctionInfo(BuiltinFunction function, ProjectState projectState) + : base(ClrModule.GetPythonType(typeof(BuiltinFunction)), projectState) { + // TODO: get return information, parameters, members + _function = function; + + _returnTypes = Utils.GetReturnTypes(function, projectState); + _doc = null; + } + + public override ISet Call(Node node, Interpreter.AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + return _returnTypes; + } + + public BuiltinFunction Function { + get { + return _function; + } + } + + public override string Description { + get { + return "built-in function " + _function.__name__; + } + } + + public override ICollection Overloads { + get { + if (_overloads == null) { + var overloads = _function.Overloads.Functions; + var result = new OverloadResult[overloads.Count]; + for (int i = 0; i < result.Length; i++) { + var bf = overloads[i] as BuiltinFunction; + result[i] = new BuiltinFunctionOverloadResult(ProjectState, bf, 0); + } + _overloads = new ReadOnlyCollection(result); + } + return _overloads; + } + } + + public override bool IsBuiltin { + get { + return true; + } + } + + public override string Documentation { + get { + if (_doc == null) { + _doc = Utils.StripDocumentation(_function.__doc__); + } + return _doc; + } + } + + public override ResultType ResultType { + get { + return ResultType.Function; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinInstanceInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinInstanceInfo.cs new file mode 100644 index 0000000000..535aa2fcb1 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinInstanceInfo.cs @@ -0,0 +1,104 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using IronPython.Runtime.Types; +using Microsoft.PyAnalysis.Interpreter; +using Microsoft.Scripting; + +namespace Microsoft.PyAnalysis.Values { + class BuiltinInstanceInfo : BuiltinNamespace, IReferenceableContainer { + private readonly BuiltinClassInfo _klass; + + public BuiltinInstanceInfo(BuiltinClassInfo klass) + : base(klass.VariableDict) { + _klass = klass; + _type = klass._type; + } + + public override bool IsBuiltin { + get { + return true; + } + } + + public override ICollection Overloads { + get { + // TODO: look for __call__ and return overloads + return base.Overloads; + } + } + + public override string Description { + get { + return PythonType.Get__name__(_klass._type); + } + } + + public override string Documentation { + get { + return _klass.Documentation; + } + } + + public override ResultType ResultType { + get { + switch (_klass.ResultType) { + case ResultType.Enum: return ResultType.EnumInstance; + case ResultType.Delegate: return ResultType.DelegateInstance; + default: + return ResultType.Instance; + } + } + } + + public override ISet GetIndex(Node node, AnalysisUnit unit, ISet index) { + // TODO: look for __getitem__, index, get result + return base.GetIndex(node, unit, index); + } + + public override ISet GetMember(Node node, AnalysisUnit unit, string name) { + var res = base.GetMember(node, unit, name); + if (res.Count > 0) { + _klass.AddMemberReference(node, unit, name); + return res.GetDescriptor(this, unit); + } + return res; + } + + public override void SetMember(Node node, AnalysisUnit unit, string name, ISet value) { + var res = base.GetMember(node, unit, name); + if (res.Count > 0) { + _klass.AddMemberReference(node, unit, name); + } + } + + public override IDictionary> GetAllMembers(bool showClr) { + Dictionary> res = new Dictionary>(); + foreach (var keyValue in base.GetAllMembers(showClr)) { + res[keyValue.Key] = keyValue.Value.GetDescriptor(this, null); + } + return res; + } + + #region IReferenceableContainer Members + + public IEnumerable GetDefinitions(string name) { + return _klass.GetDefinitions(name); + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinMethodInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinMethodInfo.cs new file mode 100644 index 0000000000..ea5d2a05c0 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinMethodInfo.cs @@ -0,0 +1,123 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Text; +using IronPython.Compiler.Ast; +using IronPython.Runtime; +using IronPython.Runtime.Operations; +using IronPython.Runtime.Types; + +namespace Microsoft.PyAnalysis.Values { + internal class BuiltinMethodInfo : BuiltinNamespace { + private BuiltinMethodDescriptor _method; + private string _doc; + private readonly ISet _returnTypes; + private BoundBuiltinMethodInfo _boundMethod; + private OverloadResult[] _overloads; + + public BuiltinMethodInfo(BuiltinMethodDescriptor method, ProjectState projectState) + : base(ClrModule.GetPythonType(typeof(BuiltinMethodDescriptor)), projectState) { + // TODO: get return information, parameters, members + _method = method; + + var function = PythonOps.GetBuiltinMethodDescriptorTemplate(method); + _returnTypes = Utils.GetReturnTypes(function, projectState); + _doc = null; + } + + public override ISet Call(Node node, Interpreter.AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + return _returnTypes; + } + + public override ISet GetDescriptor(Namespace instance, Interpreter.AnalysisUnit unit) { + if (_boundMethod == null) { + _boundMethod = new BoundBuiltinMethodInfo(this); + } + + return _boundMethod.SelfSet; + } + + public override string Description { + get { + return "built-in method " + _method.__name__; + } + } + + public ISet ReturnTypes { + get { + return _returnTypes; + } + } + + public BuiltinFunction[] BuiltinFunctions { + get { + var func = PythonOps.GetBuiltinMethodDescriptorTemplate(_method); + // skip methods that are virtual base helpers (e.g. methods like + // object.Equals(Object_#1, object other)) + + var result = new List(); + foreach (var ov in func.Overloads.Functions) { + BuiltinFunction overload = (ov as BuiltinFunction); + if (overload.Overloads.Targets[0].DeclaringType.IsAssignableFrom(_method.DeclaringType) || + overload.Overloads.Targets[0].DeclaringType.FullName.StartsWith("IronPython.Runtime.Operations.")) { + result.Add(overload); + } + } + return result.ToArray(); + } + } + + public override ICollection Overloads { + get { + if (_overloads == null) { + var overloads = BuiltinFunctions; + var result = new OverloadResult[overloads.Length]; + for (int i = 0; i < result.Length; i++) { + result[i] = new BuiltinFunctionOverloadResult(ProjectState, overloads[i], 0, new ParameterResult("self")); + } + _overloads = result; + } + return _overloads; + } + } + + public override bool IsBuiltin { + get { + return true; + } + } + + public override string Documentation { + get { + if (_doc == null) { + var doc = new StringBuilder(); + foreach (var overload in BuiltinFunctions) { + doc.Append(Utils.StripDocumentation(overload.__doc__)); + } + _doc = doc.ToString(); + } + return _doc; + } + } + + public override ResultType ResultType { + get { + return ResultType.Method; + } + } + + public string Name { get { return _method.__name__; } } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinModule.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinModule.cs new file mode 100644 index 0000000000..05b30e3b65 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinModule.cs @@ -0,0 +1,77 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using IronPython.Runtime; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis.Values { + internal class BuiltinModule : BuiltinNamespace, IReferenceableContainer { + private readonly string _name; + private readonly MemberReferences _references = new MemberReferences(); + + public BuiltinModule(PythonModule module, ProjectState projectState, bool showClr) + : base(new LazyDotNetDict(new object[] { module }, projectState, showClr)) { + object name; + if (!module.Get__dict__().TryGetValue("__name__", out name) || !(name is string)) { + _name = String.Empty; + } else { + _name = name as string; + } + } + + public override ISet GetMember(Node node, AnalysisUnit unit, string name) { + var res = base.GetMember(node, unit, name); + if (res.Count > 0) { + _references.AddReference(node, unit, name); + } + return res; + } + + public override IDictionary> GetAllMembers(bool showClr) { + // no showClr filtering on modules + return VariableDict; + } + + public override string Description { + get { + return "built-in module " + _name; + } + } + + public string Name { + get { + return _name; + } + } + + public override bool IsBuiltin { + get { return true; } + } + + public override ResultType ResultType { + get { return ResultType.Module; } + } + + #region IReferenceableContainer Members + + public IEnumerable GetDefinitions(string name) { + return _references.GetDefinitions(name); + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinNamespace.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinNamespace.cs new file mode 100644 index 0000000000..932196d0b6 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinNamespace.cs @@ -0,0 +1,81 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Interpreter; +using IronPython.Runtime.Types; + +namespace Microsoft.PyAnalysis.Values { + /// + /// Base class for things which get their members primarily via a built-in .NET type. + /// + class BuiltinNamespace : Namespace { + private readonly LazyDotNetDict _dict; + internal PythonType _type; + + public BuiltinNamespace(LazyDotNetDict dict) { + _dict = dict; + } + + public BuiltinNamespace(PythonType pythonType, ProjectState projectState) { + // TODO: Complete member initialization + _type = pythonType; + _dict = new LazyDotNetDict(_type, projectState, true); + } + + public override ISet GetMember(Node node, AnalysisUnit unit, string name) { + bool showClr = (unit != null) ? unit.DeclaringModule.ShowClr : false; + var res = VariableDict.GetClr(name, showClr, null); + if (res != null) { + return res; + } + return EmptySet.Instance; + } + + public override IDictionary> GetAllMembers(bool showClr) { + if (showClr) { + return VariableDict; + } + + var alwaysAvail = Utils.DirHelper(_type, false); + var result = new Dictionary>(VariableDict.Count); + foreach (var name in alwaysAvail) { + ISet value; + if (VariableDict.TryGetValue(name, out value)) { + result[name] = value; + } + } + + return result; + } + + public override PythonType PythonType { + get { return _type; } + } + + public LazyDotNetDict VariableDict { + get { + return _dict; + } + } + + public ProjectState ProjectState { + get { + return _dict.ProjectState; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinPropertyInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinPropertyInfo.cs new file mode 100644 index 0000000000..c875217458 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/BuiltinPropertyInfo.cs @@ -0,0 +1,71 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronPython.Runtime; +using IronPython.Runtime.Types; + +namespace Microsoft.PyAnalysis.Values { + internal class BuiltinPropertyInfo : BuiltinNamespace { + private readonly ReflectedGetterSetter _value; + private string _doc; + + public BuiltinPropertyInfo(ReflectedGetterSetter value, ProjectState projectState) + : base(new LazyDotNetDict(value.PropertyType, projectState, true)) { + _value = value; + _doc = null; + _type = _value.PropertyType; + } + + public override ISet GetDescriptor(Namespace instance, Interpreter.AnalysisUnit unit) { + return ((BuiltinClassInfo)ProjectState.GetNamespaceFromObjects(_value.PropertyType)).Instance.SelfSet; + } + + public override ISet GetStaticDescriptor(Interpreter.AnalysisUnit unit) { + ReflectedProperty rp = _value as ReflectedProperty; + if (rp != null && (rp.Info.GetGetMethod() ?? rp.Info.GetSetMethod()).IsStatic) { + BuiltinClassInfo klass = (BuiltinClassInfo)ProjectState.GetNamespaceFromObjects(rp.PropertyType); + return klass.Instance.SelfSet; + } + + return base.GetStaticDescriptor(unit); + } + + public override string Description { + get { + var typeName = _type.__repr__(ProjectState.CodeContext); + return "property of type " + typeName; + } + } + + public override ResultType ResultType { + get { + return ResultType.Property; + } + } + + public override string Documentation { + get { + if (_doc == null) { + if (_value is ReflectedProperty) { + _doc = Utils.StripDocumentation(((ReflectedProperty)_value).__doc__); + } else { + _doc = Utils.StripDocumentation(((ReflectedExtensionProperty)_value).__doc__); + } + } + return _doc; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ClassInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ClassInfo.cs new file mode 100644 index 0000000000..e534fc97a6 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ClassInfo.cs @@ -0,0 +1,360 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using IronPython.Runtime.Types; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis.Values { + internal class ClassInfo : UserDefinedInfo, IReferenceableContainer { + private readonly List> _bases; + private readonly InstanceInfo _instanceInfo; + private readonly ClassScope _scope; + private readonly ProjectEntry _entry; + private readonly int _declVersion; + private ReferenceDict _references; + + internal ClassInfo(AnalysisUnit unit, ProjectEntry entry) + : base(unit) { + _instanceInfo = new InstanceInfo(this); + _bases = new List>(); + _entry = entry; + _scope = new ClassScope(this); + _declVersion = entry.Version; + } + + public override ISet Call(Node node, AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + if (unit != null) { + AddCall(node, keywordArgNames, unit, args); + } + + return _instanceInfo.SelfSet; + } + + private void AddCall(Node node, string[] keywordArgNames, AnalysisUnit unit, ISet[] argumentVars) { + var init = GetMember(node, unit, "__init__"); + var initArgs = Utils.Concat(_instanceInfo.SelfSet, argumentVars); + + foreach (var initFunc in init) { + initFunc.Call(node, unit, initArgs, keywordArgNames); + } + + // TODO: If we checked for metaclass, we could pass it in as the cls arg here + var n = GetMember(node, unit, "__new__"); + var newArgs = Utils.Concat(EmptySet.Instance, argumentVars); + foreach (var newFunc in n) { + // TODO: Really we should be returning the result of __new__ if it's overridden + newFunc.Call(node, unit, newArgs, keywordArgNames); + } + } + + public ClassDefinition ClassDefinition { + get { return _analysisUnit.Ast as ClassDefinition; } + } + + public override string ShortDescription { + get { + return ClassDefinition.Name; + } + } + + public override string Description { + get { + var res = "class " + ClassDefinition.Name; + if (!String.IsNullOrEmpty(Documentation)) { + res += Environment.NewLine + Documentation; + } + return res; + } + } + + public override string Documentation { + get { + if (ClassDefinition.Body != null) { + return ClassDefinition.Body.Documentation; + } + return ""; + } + } + + public override LocationInfo Location { + get { + return new LocationInfo(_entry, ClassDefinition.Start.Line, ClassDefinition.Start.Column, ClassDefinition.Header.Index - ClassDefinition.Start.Index); + } + } + + public override PythonType PythonType { + get { + return TypeCache.PythonType; + } + } + + public override IProjectEntry DeclaringModule { + get { + return _entry; + } + } + + public override int DeclaringVersion { + get { + return _declVersion; + } + } + + public override ICollection Overloads { + get { + var result = new List(); + VariableDef init; + if (Scope.Variables.TryGetValue("__init__", out init)) { + // this type overrides __init__, display that for it's help + foreach (var initFunc in init.Types) { + foreach (var overload in initFunc.Overloads) { + result.Add(GetInitOverloadResult(overload)); + } + } + } + + VariableDef @new; + if (Scope.Variables.TryGetValue("__new__", out @new)) { + foreach (var newFunc in @new.Types) { + foreach (var overload in newFunc.Overloads) { + result.Add(GetNewOverloadResult(overload)); + } + } + } + + if (result.Count == 0) { + foreach (var baseClass in _bases) { + foreach (var ns in baseClass) { + foreach (var overload in ns.Overloads) { + result.Add( + new OverloadResult( + overload.Parameters, + ClassDefinition.Name + ) + ); + } + } + } + } + + if (result.Count == 0) { + // Old style class? + result.Add(new OverloadResult(new ParameterResult[0], ClassDefinition.Name)); + } + + // TODO: Filter out duplicates? + return result; + } + } + + private SimpleOverloadResult GetNewOverloadResult(OverloadResult overload) { + var doc = overload.Documentation; + return new SimpleOverloadResult( + overload.Parameters.RemoveFirst(), + ClassDefinition.Name, + String.IsNullOrEmpty(doc) ? ClassDefinition.Body.Documentation : doc + ); + } + + private SimpleOverloadResult GetInitOverloadResult(OverloadResult overload) { + var doc = overload.Documentation; + return new SimpleOverloadResult( + overload.Parameters.RemoveFirst(), + ClassDefinition.Name, + String.IsNullOrEmpty(doc) ? ClassDefinition.Body.Documentation : doc + ); + } + + public List> Bases { + get { + return _bases; + } + } + + public InstanceInfo Instance { + get { + return _instanceInfo; + } + } + + public override IDictionary> GetAllMembers(bool showClr) { + var result = new Dictionary>(Scope.Variables.Count); + foreach (var v in Scope.Variables) { + result[v.Key] = v.Value.Types; + } + + foreach (var b in _bases) { + foreach (var ns in b) { + if (ns.Push()) { + try { + foreach (var kvp in ns.GetAllMembers(showClr)) { + ISet existing; + if (!result.TryGetValue(kvp.Key, out existing) || existing.Count == 0) { + result[kvp.Key] = kvp.Value; + } else { + HashSet tmp = new HashSet(); + tmp.UnionWith(existing); + tmp.UnionWith(kvp.Value); + + result[kvp.Key] = tmp; + } + } + } finally { + ns.Pop(); + } + } + } + } + return result; + } + + internal override void AddReference(Node node, AnalysisUnit unit) { + if (!unit.ForEval) { + if (_references == null) { + _references = new ReferenceDict(); + } + _references.GetReferences(unit.DeclaringModule.ProjectEntry).References.Add(new SimpleSrcLocation(node.Span)); + } + } + + public override ISet GetMember(Node node, AnalysisUnit unit, string name) { + return GetMemberNoReferences(node, unit, name); + } + + public ISet GetMemberNoReferences(Node node, AnalysisUnit unit, string name) { + ISet result = null; + bool ownResult = false; + var v = Scope.GetVariable(node, unit, name); + if (v != null) { + ownResult = false; + result = v.Types; + } + + // TODO: Need to search MRO, not bases + foreach (var baseClass in _bases) { + foreach (var baseRef in baseClass) { + if (baseRef.Push()) { + try { + ClassInfo klass = baseRef as ClassInfo; + ISet baseMembers; + if (klass != null) { + baseMembers = klass.GetMember(node, unit, name); + } else { + BuiltinClassInfo builtinClass = baseRef as BuiltinClassInfo; + if (builtinClass != null) { + baseMembers = builtinClass.GetMember(node, unit, name); + } else { + baseMembers = baseRef.GetMember(node, unit, name); + } + } + + AddNewMembers(ref result, ref ownResult, baseMembers); + } finally { + baseRef.Pop(); + } + } + } + } + return result ?? EmptySet.Instance; + } + + public override void SetMember(Node node, AnalysisUnit unit, string name, ISet value) { + var variable = Scope.CreateVariable(node, unit, name, false); + variable.AddAssignment(node, unit); + variable.AddTypes(node, unit, value); + } + + public override void DeleteMember(Node node, AnalysisUnit unit, string name) { + var v = Scope.GetVariable(node, unit, name); + if (v != null) { + v.AddReference(node, unit); + } + } + + private static void AddNewMembers(ref ISet result, ref bool ownResult, ISet newMembers) { + if (!ownResult) { + if (result != null) { + // merging with unowned set for first time + if (result.Count == 0) { + result = newMembers; + } else if (newMembers.Count != 0) { + result = new HashSet(result); + result.UnionWith(newMembers); + ownResult = true; + } + } else { + // getting members for the first time + result = newMembers; + } + } else { + // just merging in the new members + result.UnionWith(newMembers); + } + } + + public override ResultType ResultType { + get { + return ResultType.Class; + } + } + + public override string ToString() { + return "user class (" + ClassDefinition.Name /* + hex(id(self))*/ + ")"; + } + + public ClassScope Scope { + get { + return _scope; + } + } + + #region IVariableDefContainer Members + + public IEnumerable GetDefinitions(string name) { + VariableDef def; + if (_scope.Variables.TryGetValue(name, out def)) { + yield return def; + } + + foreach (var baseClassSet in Bases) { + foreach (var baseClass in baseClassSet) { + IReferenceableContainer container = baseClass as IReferenceableContainer; + if (container != null) { + foreach (var baseDef in container.GetDefinitions(name)) { + yield return baseDef; + } + } + } + } + } + + #endregion + + #region IReferenceable Members + + public override IEnumerable References { + get { + if (_references != null) { + return _references.AllReferences; + } + return new LocationInfo[0]; + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ConstantInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ConstantInfo.cs new file mode 100644 index 0000000000..8889a6cbb4 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ConstantInfo.cs @@ -0,0 +1,145 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronPython.Compiler; +using IronPython.Runtime.Types; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis.Values { + internal class ConstantInfo : BuiltinInstanceInfo { + private readonly object _value; + private readonly Namespace _builtinInfo; + private string _doc; + + public ConstantInfo(object value, ProjectState projectState) + : base((BuiltinClassInfo)projectState.GetNamespaceFromObjects(DynamicHelpers.GetPythonType(value))) { + _value = value; + _type = DynamicHelpers.GetPythonType(value); + _builtinInfo = ((BuiltinClassInfo)projectState.GetNamespaceFromObjects(_type)).Instance; + } + + public override ISet BinaryOperation(IronPython.Compiler.Ast.Node node, AnalysisUnit unit, PythonOperator operation, ISet rhs) { + return _builtinInfo.BinaryOperation(node, unit, operation, rhs); + } + + public override ISet UnaryOperation(IronPython.Compiler.Ast.Node node, AnalysisUnit unit, PythonOperator operation) { + return _builtinInfo.UnaryOperation(node, unit, operation); + } + + public override ISet Call(IronPython.Compiler.Ast.Node node, AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + return _builtinInfo.Call(node, unit, args, keywordArgNames); + } + + public override void AugmentAssign(IronPython.Compiler.Ast.AugmentedAssignStatement node, AnalysisUnit unit, ISet value) { + _builtinInfo.AugmentAssign(node, unit, value); + } + + public override ISet GetDescriptor(Namespace instance, AnalysisUnit unit) { + return _builtinInfo.GetDescriptor(instance, unit); + } + + public override ISet GetMember(IronPython.Compiler.Ast.Node node, AnalysisUnit unit, string name) { + return _builtinInfo.GetMember(node, unit, name); + } + + public override void SetMember(IronPython.Compiler.Ast.Node node, AnalysisUnit unit, string name, ISet value) { + _builtinInfo.SetMember(node, unit, name, value); + } + + public override ISet GetIndex(IronPython.Compiler.Ast.Node node, AnalysisUnit unit, ISet index) { + return base.GetIndex(node, unit, index); + } + + public override void SetIndex(IronPython.Compiler.Ast.Node node, AnalysisUnit unit, ISet index, ISet value) { + _builtinInfo.SetIndex(node, unit, index, value); + } + + public override ISet GetStaticDescriptor(AnalysisUnit unit) { + return _builtinInfo.GetStaticDescriptor(unit); + } + + public override IDictionary> GetAllMembers(bool showClr) { + return _builtinInfo.GetAllMembers(showClr); + } + + public override string Description { + get { + if (_value == null) { + return "None"; + } + + return PythonType.Get__name__(_type); + //return PythonOps.Repr(ProjectState.CodeContext, _value); + } + } + + public override string Documentation { + get { + if (_doc == null) { + object docObj = PythonType.Get__doc__(ProjectState.CodeContext, _type); + _doc = docObj == null ? "" : Utils.StripDocumentation(docObj.ToString()); + } + return _doc; + } + } + + public override bool IsBuiltin { + get { + return true; + } + } + + public override ResultType ResultType { + get { + return ResultType.Constant; + } + } + + public override string ToString() { + return ""; // " at " + hex(id(self)) + } + + public override object GetConstantValue() { + return _value; + } + + public override bool UnionEquals(Namespace ns) { + ConstantInfo ci = ns as ConstantInfo; + if (ci == null) { + return false; + } else if (ci._value == _value) { + return true; + } else if (_value == null) { + return false; + } + + return _value.GetType() == ci._value.GetType(); + } + + public override int UnionHashCode() { + if (_value == null) { + return 0; + } + + return _value.GetType().GetHashCode(); + } + + public object Value { + get { + return _value; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/DependencyInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/DependencyInfo.cs new file mode 100644 index 0000000000..d2bed6a120 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/DependencyInfo.cs @@ -0,0 +1,124 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.PyAnalysis.Interpreter; +using Microsoft.Scripting; + +namespace Microsoft.PyAnalysis.Values { + /// + /// contains information about dependencies. Each DependencyInfo is + /// attached to a VariableRef in a dictionary keyed off of the ProjectEntry. + /// + /// Module -> The Module this DependencyInfo object tracks. + /// DependentUnits -> What needs to change if this VariableRef is updated. + /// Types -> Types that this VariableRef has received from the Module. + /// + internal class DependencyInfo { + private readonly int _version; + private HashSet _dependentUnits; + + public DependencyInfo(int version) { + _version = version; + _dependentUnits = new HashSet(); + } + + public HashSet DependentUnits { + get { + if (_dependentUnits == null) { + _dependentUnits = new HashSet(); + } + return _dependentUnits; + } + } + + public void AddDependentUnit(AnalysisUnit unit) { + if (_dependentUnits != null) { + var checking = unit; + while (checking != null) { + if (_dependentUnits.Contains(checking)) { + return; + } + checking = checking.Parent; + } + } else { + _dependentUnits = new HashSet(); + } + _dependentUnits.Add(unit); + } + + public int Version { + get { + return _version; + } + } + } + + internal class TypedDependencyInfo : DependencyInfo { + private TypeUnion _union; + public HashSet _references, _assignments; + + public TypedDependencyInfo(int version) + : base(version) { + } + + public TypeUnion Types { + get { + if (_union == null) { + _union = new TypeUnion(); + } + return _union; + } + set { + _union = value; + } + } + + public bool HasReferences { + get { + return _references != null; + } + } + + public HashSet References { + get { + if (_references == null) { + _references = new HashSet(); + } + return _references; + } + set { + _references = value; + } + } + + public bool HasAssignments { + get { + return _assignments != null; + } + } + + public HashSet Assignments { + get { + if (_assignments == null) { + _assignments = new HashSet(); + } + return _assignments; + } + set { + _assignments = value; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/DictionaryInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/DictionaryInfo.cs new file mode 100644 index 0000000000..6725a04faa --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/DictionaryInfo.cs @@ -0,0 +1,124 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using IronPython.Runtime; +using IronPython.Runtime.Types; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis.Values { + internal class DictionaryInfo : BuiltinInstanceInfo { + private readonly ISet _keyTypes; + private readonly ISet _valueTypes; + private ISet _getMethod; + + public DictionaryInfo(HashSet keyTypes, HashSet valueTypes, ProjectState projectState, bool showClr) + : base(projectState._dictType) { + _keyTypes = keyTypes; + _valueTypes = valueTypes; + _getMethod = null; + } + + class DictionaryGetMethod : BuiltinMethodInfo { + private readonly DictionaryInfo _myDict; + + internal DictionaryGetMethod(BuiltinMethodDescriptor method, ProjectState projectState, DictionaryInfo myDict) + : base(method, projectState) { + _myDict = myDict; + } + + public override ISet Call(Node node, AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + if (args.Length <= 1) { + return _myDict._valueTypes; + } + + return _myDict._valueTypes.Union(args[1]); + } + + public override bool IsBuiltin { + get { + return false; + } + } + } + + public override ISet GetIndex(Node node, AnalysisUnit unit, ISet index) { + return _valueTypes; + } + + public override void SetIndex(Node node, AnalysisUnit unit, ISet index, ISet value) { + _keyTypes.UnionWith(index); + _valueTypes.UnionWith(value); + } + + public override ISet GetMember(Node node, AnalysisUnit unit, string name) { + if (name == "get") { + if (_getMethod == null) { + var getter = ProjectState.GetMember(ClrModule.GetPythonType(typeof(PythonDictionary)), "get"); + _getMethod = new DictionaryGetMethod(getter, ProjectState, this).SelfSet; + } + return _getMethod; + } + + return base.GetMember(node, unit, name); + } + + public override string ShortDescription { + get { + return "dict"; + } + } + + public override string Description { + get { + // dict({k : v}) + Namespace keyType = _keyTypes.GetUnionType(); + string keyName = keyType == null ? null : keyType.ShortDescription; + Namespace valueType = _valueTypes.GetUnionType(); + string valueName = valueType == null ? null : valueType.ShortDescription; + + if (keyName != null || valueName != null) { + return "dict({" + + (keyName ?? "unknown") + + " : " + + (valueName ?? "unknown") + + "}"; + } + + return "dict"; + } + } + + public override bool IsBuiltin { + get { + return false; + } + } + + public override bool UnionEquals(Namespace ns) { + return ns is DictionaryInfo; + } + + public override int UnionHashCode() { + return 2; + } + + public override ResultType ResultType { + get { + return ResultType.Field; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/EnumInstanceInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/EnumInstanceInfo.cs new file mode 100644 index 0000000000..3f28720829 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/EnumInstanceInfo.cs @@ -0,0 +1,36 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.PyAnalysis.Values { + internal class EnumInstanceInfo : ConstantInfo { + + public EnumInstanceInfo(object value, ProjectState projectState) + : base(value, projectState) { + } + + public override string Description { + get { + return Value.ToString(); + } + } + + public override ResultType ResultType { + get { + return ResultType.EnumInstance; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/FunctionInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/FunctionInfo.cs new file mode 100644 index 0000000000..b617a4ab0f --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/FunctionInfo.cs @@ -0,0 +1,419 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Text; +using IronPython.Compiler.Ast; +using IronPython.Runtime; +using IronPython.Runtime.Operations; +using Microsoft.PyAnalysis.Interpreter; +using System; + +namespace Microsoft.PyAnalysis.Values { + internal class FunctionInfo : UserDefinedInfo, IReferenceableContainer { + private readonly ProjectEntry _entry; + private Dictionary> _methods; + private Dictionary _functionAttrs; + private GeneratorInfo _generator; + private VariableDef _returnValue; + public bool IsStatic; + public bool IsClassMethod; + public bool IsProperty; + private ReferenceDict _references; + private readonly int _declVersion; + [ThreadStatic] + private static List _descriptionStack; + + internal FunctionInfo(AnalysisUnit unit, ProjectEntry entry) + : base(unit) { + _entry = entry; + _returnValue = new VariableDef(); + _declVersion = entry.Version; + // TODO: pass NoneInfo if we can't determine the function always returns + } + + public ProjectEntry ProjectEntry { + get { + return _entry; + } + } + + public override IProjectEntry DeclaringModule { + get { + return _entry; + } + } + + public override int DeclaringVersion { + get { + return _declVersion; + } + } + + public override ISet Call(Node node, AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + if (unit != null) { + AddCall(node, keywordArgNames, unit, args); + } + + if (_generator != null) { + return _generator.SelfSet; + } + + return ReturnValue.Types; + } + + private void AddCall(Node node, string[] keywordArgNames, AnalysisUnit unit, ISet[] args) { + ReturnValue.AddDependency(unit); + + if (ParameterTypes != null) { + bool added = false; + + // TODO: Warn when a keyword argument is provided and it maps to + // something which is also a positional argument: + // def f(a, b, c): + // print a, b, c + // + // f(2, 3, a=42) + + if (PropagateCall(node, keywordArgNames, unit, args, added)) { + // new inputs to the function, it needs to be analyzed. + _analysisUnit.Enqueue(); + } + } + } + + private bool PropagateCall(Node node, string[] keywordArgNames, AnalysisUnit unit, ISet[] args, bool added) { + for (int i = 0; i < args.Length; i++) { + int kwIndex = i - (args.Length - keywordArgNames.Length); + if (kwIndex >= 0) { + string curArg = keywordArgNames[kwIndex]; + switch (curArg) { + case "*": + int lastPos = args.Length - keywordArgNames.Length; + foreach (var type in args[i]) { + int? argLen = type.GetLength(); + if (argLen != null) { + for (int j = 0; j < argLen.Value; j++) { + var indexType = type.GetIndex(node, unit, _analysisUnit.ProjectState.GetConstant(j)); + + int paramIndex = lastPos + j; + if (paramIndex >= ParameterTypes.Length) { + break; + } else if (ParameterTypes[lastPos + j].AddTypes(FunctionDefinition.Parameters[lastPos + j], unit, indexType)) { + added = true; + } + } + } + } + break; + case "**": + // TODO: Handle keyword argument splatting + break; + default: + for (int j = 0; j < ParameterTypes.Length; j++) { + string paramName = GetParameterName(j); + if (paramName == curArg) { + if (ParameterTypes[j].AddTypes(FunctionDefinition.Parameters[j], unit, args[i])) { + added = true; + break; + } + } + } + // TODO: Report a warning if we don't find the keyword argument and we don't + // have a ** parameter. + break; + } + } else if (i < ParameterTypes.Length) { + // positional argument + if (ParameterTypes[i].AddTypes(FunctionDefinition.Parameters[i], unit, args[i])) { + added = true; + } + + } // else we should warn too many arguments + } + return added; + } + + public override string Description { + get { + StringBuilder result = new StringBuilder("def "); + result.Append(FunctionDefinition.Name); + result.Append("(...)"); // TOOD: Include parameter information? + if (!String.IsNullOrEmpty(Documentation)) { + result.AppendLine(); + result.Append(Documentation); + } + return result.ToString(); + } + } + + public string FunctionDescription { + get { + StringBuilder result = new StringBuilder(); + bool first = true; + foreach (var ns in ReturnValue.Types) { + if (ns == null || ns.Description == null) { + continue; + } + + if (first) { + result.Append(" -> "); + first = false; + } else { + result.Append(", "); + } + AppendDescription(result, ns); + } + //result.Append(GetDependencyDisplay()); + return result.ToString(); + } + } + + private static void AppendDescription(StringBuilder result, Namespace key) { + if (DescriptionStack.Contains(key)) { + result.Append("..."); + } else { + DescriptionStack.Add(key); + try { + result.Append(key.Description); + } finally { + DescriptionStack.Pop(); + } + } + } + + private static List DescriptionStack { + get { + if (_descriptionStack == null) { + _descriptionStack = new List(); + } + return _descriptionStack; + } + } + + public FunctionDefinition FunctionDefinition { + get { + return (_analysisUnit.Ast as FunctionDefinition); + } + } + + public override ISet GetDescriptor(Namespace instance, AnalysisUnit unit) { + if (instance == null || IsStatic) { + return SelfSet; + } + if (IsProperty) { + return ReturnValue.Types; + } + + if (_methods == null) { + _methods = new Dictionary>(); + } + + ISet result; + if (!_methods.TryGetValue(instance, out result) || result == null) { + _methods[instance] = result = new MethodInfo(this, instance).SelfSet; + } + return result; + } + + public override string Documentation { + get { + if (FunctionDefinition.Body != null) { + return FunctionDefinition.Body.Documentation; + } + return ""; + } + } + + public override ResultType ResultType { + get { + return IsProperty ? ResultType.Property : ResultType.Function; + } + } + + public override string ToString() { + return "FunctionInfo" /* + hex(id(this)) */ + " " + FunctionDefinition.Name; + } + + public override LocationInfo Location { + get { + return new LocationInfo( + _entry, + FunctionDefinition.Start.Line, + FunctionDefinition.Start.Column, + FunctionDefinition.Header.Index - FunctionDefinition.Start.Index); + } + } + + public override ICollection Overloads { + get { + var parameters = new ParameterResult[FunctionDefinition.Parameters.Count]; + for (int i = 0; i < FunctionDefinition.Parameters.Count; i++) { + var curParam = FunctionDefinition.Parameters[i]; + var newParam = MakeParameterResult(_entry.ProjectState, curParam); + parameters[i] = newParam; + } + + return new OverloadResult[] { + new SimpleOverloadResult(parameters, FunctionDefinition.Name, Documentation) + }; + } + } + + internal static ParameterResult MakeParameterResult(ProjectState state, Parameter curParam) { + string name = curParam.Name; + if (curParam.IsDictionary) { + name = "**" + name; + } else if (curParam.IsList) { + name = "*" + curParam.Name; + } + + if (curParam.DefaultValue != null) { + // TODO: Support all possible expressions for default values, we should + // probably have a PythonAst walker for expressions or we should add ToCodeString() + // onto Python ASTs so they can round trip + ConstantExpression defaultValue = curParam.DefaultValue as ConstantExpression; + if (defaultValue != null) { + name = name + " = " + PythonOps.Repr(state.CodeContext, defaultValue.Value); + } + + NameExpression nameExpr = curParam.DefaultValue as NameExpression; + if (nameExpr != null) { + name = name + " = " + nameExpr.Name; + } + + DictionaryExpression dict = curParam.DefaultValue as DictionaryExpression; + if (dict != null) { + if (dict.Items.Count == 0) { + name = name + " = {}"; + } else { + name = name + " = {...}"; + } + } + + ListExpression list = curParam.DefaultValue as ListExpression; + if (list != null) { + if (list.Items.Count == 0) { + name = name + " = []"; + } else { + name = name + " = [...]"; + } + } + + TupleExpression tuple = curParam.DefaultValue as TupleExpression; + if (tuple != null) { + if (tuple.Items.Count == 0) { + name = name + " = ()"; + } else { + name = name + " = (...)"; + } + } + } + + var newParam = new ParameterResult(name); + return newParam; + } + + public override void SetMember(Node node, AnalysisUnit unit, string name, ISet value) { + if (_functionAttrs == null) { + _functionAttrs = new Dictionary(); + } + + VariableDef varRef; + if (!_functionAttrs.TryGetValue(name, out varRef)) { + _functionAttrs[name] = varRef = new VariableDef(); + } + varRef.AddAssignment(node, unit); + varRef.AddTypes(node, unit, value); + } + + public override ISet GetMember(Node node, AnalysisUnit unit, string name) { + VariableDef tmp; + if (_functionAttrs != null && _functionAttrs.TryGetValue(name, out tmp)) { + tmp.AddDependency(unit); + tmp.AddReference(node, unit); + + return tmp.Types; + } + // TODO: Create one and add a dependency + + return _entry.ProjectState._functionType.GetMember(node, unit, name); + } + + public override IDictionary> GetAllMembers(bool showClr) { + if (_functionAttrs == null || _functionAttrs.Count == 0) { + return _entry.ProjectState._functionType.GetAllMembers(showClr); + } + + var res = new Dictionary>(_entry.ProjectState._functionType.GetAllMembers(showClr)); + foreach (var variable in _functionAttrs) { + ISet existing; + if (!res.TryGetValue(variable.Key, out existing)) { + res[variable.Key] = existing = new HashSet(); + } + existing.UnionWith(variable.Value.Types); + } + return res; + } + + private string GetParameterName(int index) { + return FunctionDefinition.Parameters[index].Name; + } + + public GeneratorInfo Generator { + get { + if (_generator == null) { + _generator = new GeneratorInfo(this); + } + return _generator; + } + } + + public VariableDef ReturnValue { + get { return _returnValue; } + } + + public ProjectState ProjectState { get { return _entry.ProjectState; } } + + internal override void AddReference(Node node, AnalysisUnit unit) { + if (!unit.ForEval) { + if (_references == null) { + _references = new ReferenceDict(); + } + _references.GetReferences(unit.DeclaringModule.ProjectEntry).References.Add(new SimpleSrcLocation(node.Span)); + } + } + + public override IEnumerable References { + get { + if (_references != null) { + return _references.AllReferences; + } + return new LocationInfo[0]; + } + } + + #region IReferenceableContainer Members + + public IEnumerable GetDefinitions(string name) { + VariableDef def; + if (_functionAttrs != null && _functionAttrs.TryGetValue(name, out def)) { + return new IReferenceable[] { def }; + } + return new IReferenceable[0]; + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/GeneratorInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/GeneratorInfo.cs new file mode 100644 index 0000000000..5ee7a5fb9b --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/GeneratorInfo.cs @@ -0,0 +1,76 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis.Values { + internal class GeneratorInfo : BuiltinInstanceInfo { + private readonly FunctionInfo _functionInfo; + private readonly GeneratorNextBoundBuiltinMethodInfo _nextMethod; + private readonly GeneratorSendBoundBuiltinMethodInfo _sendMethod; + private ISet _yields = EmptySet.Instance; + private VariableDef _sends; + + public GeneratorInfo(FunctionInfo functionInfo) + : base(functionInfo.ProjectState._generatorType) { + _functionInfo = functionInfo; + var nextMeth = VariableDict["next"]; + var sendMeth = VariableDict["send"]; + + _nextMethod = new GeneratorNextBoundBuiltinMethodInfo(this, (BuiltinMethodInfo)nextMeth.First()); + _sendMethod = new GeneratorSendBoundBuiltinMethodInfo(this, (BuiltinMethodInfo)sendMeth.First()); + + _sends = new VariableDef(); + } + + public override ISet GetMember(Node node, AnalysisUnit unit, string name) { + switch(name) { + case "next": return _nextMethod.SelfSet; + case "send": return _sendMethod.SelfSet; + } + + return base.GetMember(node, unit, name); + } + + public override ISet GetEnumeratorTypes(Node node, AnalysisUnit unit) { + return _yields; + } + + public void AddYield(ISet yieldValue) { + _yields = _yields.Union(yieldValue); + } + + public void AddSend(Node node, AnalysisUnit unit, ISet sendValue) { + if (_sends.AddTypes(node, unit, sendValue)) { + _functionInfo._analysisUnit.Enqueue(); + } + } + + public ISet Yields { + get { + return _yields; + } + } + + public VariableDef Sends { + get { + return _sends; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/GeneratorNextBoundBuiltinMethodInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/GeneratorNextBoundBuiltinMethodInfo.cs new file mode 100644 index 0000000000..6bff672770 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/GeneratorNextBoundBuiltinMethodInfo.cs @@ -0,0 +1,33 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis.Values { + class GeneratorNextBoundBuiltinMethodInfo : BoundBuiltinMethodInfo { + private readonly GeneratorInfo _generator; + + public GeneratorNextBoundBuiltinMethodInfo(GeneratorInfo generator, BuiltinMethodInfo method) + : base(method) { + _generator = generator; + } + + public override ISet Call(Node node, AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + return _generator.Yields; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/GeneratorSendBoundBuiltinMethodInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/GeneratorSendBoundBuiltinMethodInfo.cs new file mode 100644 index 0000000000..1ab05e3767 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/GeneratorSendBoundBuiltinMethodInfo.cs @@ -0,0 +1,37 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis.Values { + class GeneratorSendBoundBuiltinMethodInfo : BoundBuiltinMethodInfo { + private readonly GeneratorInfo _generator; + + public GeneratorSendBoundBuiltinMethodInfo(GeneratorInfo generator, BuiltinMethodInfo method) + : base(method) { + _generator = generator; + } + + public override ISet Call(Node node, AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + if (args.Length == 1) { + _generator.AddSend(node, unit, args[0]); + } + + return _generator.Yields; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/IReferenceable.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/IReferenceable.cs new file mode 100644 index 0000000000..454ce1598c --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/IReferenceable.cs @@ -0,0 +1,31 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; + +namespace Microsoft.PyAnalysis.Values { + interface IReferenceableContainer { + IEnumerable GetDefinitions(string name); + } + + interface IReferenceable { + IEnumerable> Definitions { + get; + } + IEnumerable> References { + get; + } + } + +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/InstanceInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/InstanceInfo.cs new file mode 100644 index 0000000000..f9113d79e6 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/InstanceInfo.cs @@ -0,0 +1,192 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Interpreter; +using Microsoft.Scripting; + +namespace Microsoft.PyAnalysis.Values { + /// + /// Represents a class implemented in Python + /// + internal class InstanceInfo : Namespace, IReferenceableContainer { + private readonly ClassInfo _classInfo; + private Dictionary _instanceAttrs; + + public InstanceInfo(ClassInfo classInfo) { + _classInfo = classInfo; + } + + public override IDictionary> GetAllMembers(bool showClr) { + var res = new Dictionary>(); + if (_instanceAttrs != null) { + foreach (var kvp in _instanceAttrs) { + var types = kvp.Value.Types; + var key = kvp.Key; + + if (types.Count > 0) { + MergeTypes(res, key, types); + } + } + } + + foreach (var classMem in _classInfo.GetAllMembers(showClr)) { + MergeTypes(res, classMem.Key, classMem.Value); + } + return res; + } + + private static void MergeTypes(Dictionary> res, string key, IEnumerable types) { + ISet set; + if (!res.TryGetValue(key, out set)) { + res[key] = set = new HashSet(); + } + + set.UnionWith(types); + } + + public Dictionary InstanceAttributes { + get { + return _instanceAttrs; + } + } + + public override ISet GetMember(Node node, AnalysisUnit unit, string name) { + // __getattribute__ takes precedence over everything. + ISet getattrRes = EmptySet.Instance; + var getAttribute = _classInfo.GetMemberNoReferences(node, unit.CopyForEval(), "__getattribute__"); + if (getAttribute.Count > 0) { + foreach (var getAttrFunc in getAttribute) { + var func = getAttrFunc as BuiltinMethodInfo; + if (func != null && func.BuiltinFunctions.Length == 1 && func.BuiltinFunctions[0].DeclaringType == typeof(object)) { + continue; + } + // TODO: We should really do a get descriptor / call here + getattrRes = getattrRes.Union(getAttrFunc.Call(node, unit, new[] { SelfSet, _classInfo._analysisUnit.ProjectState._stringType.Instance.SelfSet }, Microsoft.Scripting.Utils.ArrayUtils.EmptyStrings)); + } + if (getattrRes.Count > 0) { + return getattrRes; + } + } + + // then check class members + var classMem = _classInfo.GetMemberNoReferences(node, unit, name).GetDescriptor(this, unit); + if (classMem.Count > 0) { + // TODO: Check if it's a data descriptor... + return classMem; + } + + // ok, it most be an instance member... + if (_instanceAttrs == null) { + _instanceAttrs = new Dictionary(); + } + VariableDef def; + if (!_instanceAttrs.TryGetValue(name, out def)) { + _instanceAttrs[name] = def = new VariableDef(); + } + def.AddReference(node, unit); + def.AddDependency(unit); + + var res = def.Types; + if (res.Count == 0) { + // and if that doesn't exist fall back to __getattr__ + var getAttr = _classInfo.GetMemberNoReferences(node, unit, "__getattr__"); + if (getAttr.Count > 0) { + foreach (var getAttrFunc in getAttr) { + // TODO: We should really do a get descriptor / call here + getattrRes = getattrRes.Union(getAttrFunc.Call(node, unit, new[] { SelfSet, _classInfo._analysisUnit.ProjectState._stringType.Instance.SelfSet }, Microsoft.Scripting.Utils.ArrayUtils.EmptyStrings)); + } + } + return getattrRes; + } + return res; + } + + public override void SetMember(Node node, AnalysisUnit unit, string name, ISet value) { + if (_instanceAttrs == null) { + _instanceAttrs = new Dictionary(); + } + + VariableDef instMember; + if (!_instanceAttrs.TryGetValue(name, out instMember) || instMember == null) { + _instanceAttrs[name] = instMember = new VariableDef(); + } + instMember.AddAssignment(node, unit); + instMember.AddTypes(node, unit, value); + } + + public override void DeleteMember(Node node, AnalysisUnit unit, string name) { + if (_instanceAttrs == null) { + _instanceAttrs = new Dictionary(); + } + + VariableDef instMember; + if (!_instanceAttrs.TryGetValue(name, out instMember) || instMember == null) { + _instanceAttrs[name] = instMember = new VariableDef(); + } + + instMember.AddReference(node, unit); + } + + public override IProjectEntry DeclaringModule { + get { + return _classInfo.DeclaringModule; + } + } + + public override int DeclaringVersion { + get { + return _classInfo.DeclaringVersion; + } + } + + public override string Description { + get { + return ClassInfo.ClassDefinition.Name + " instance"; + } + } + + public override string Documentation { + get { + return ClassInfo.Documentation; + } + } + + public override ResultType ResultType { + get { + return ResultType.Instance; + } + } + + public ClassInfo ClassInfo { + get { return _classInfo; } + } + + #region IVariableDefContainer Members + + public IEnumerable GetDefinitions(string name) { + VariableDef def; + if (_instanceAttrs != null && _instanceAttrs.TryGetValue(name, out def)) { + yield return def; + } + + foreach (var classDef in _classInfo.GetDefinitions(name)) { + yield return classDef; + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/LazyDotNetDict.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/LazyDotNetDict.cs new file mode 100644 index 0000000000..a2525674ff --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/LazyDotNetDict.cs @@ -0,0 +1,289 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Linq; +using IronPython.Runtime; +using IronPython.Runtime.Operations; +using IronPython.Runtime.Types; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.PyAnalysis.Values { + /// + /// lazily creates nested namespaces and types from a .NET namespace. This + /// just uses dir to reflect over the members and returns more Namespace objects + /// which correspond with the appropriate member type. + /// + internal class LazyDotNetDict : IDictionary> { + private readonly Dictionary> _variables; + private readonly object[] _objects; + private readonly ProjectState _projectState; + private readonly bool _showClr; + private Dictionary> _nonClrAttributes; + private bool _gotAll; + + public LazyDotNetDict(object obj, ProjectState projectState, bool showClr) + : this(new[] { obj }, projectState, showClr) { + } + + public LazyDotNetDict(IEnumerable objects, ProjectState projectState, bool showClr) { + _objects = objects.ToArray(); + _variables = new Dictionary>(); + _projectState = projectState; + _showClr = showClr; + } + + public ISet GetValue(string index, ISet defaultValue) { + return GetClr(index, _showClr, defaultValue); + } + + public bool IsVisible(object module, string index, bool showClr) { + if (showClr) { + return true; + } + + if (_nonClrAttributes == null) { + _nonClrAttributes = new Dictionary>(); + } + + IList members; + if (!_nonClrAttributes.TryGetValue(module, out members)) { + _nonClrAttributes[module] = members = Utils.DirHelper(module, false); + } + + return members.IndexOf(index) != -1; + } + + private object GetOne(object module, string index, bool showClr) { + if (IsVisible(module, index, showClr)) { + PythonType pyType = (module as PythonType); + if (pyType != null) { + foreach (var baseType in pyType.mro()) { + PythonType curType = (baseType as PythonType); + if (curType != null) { + IDictionary dict = new DictProxy(curType); + object bresult; + if (dict.TryGetValue(index, out bresult)) { + return bresult; + } + } + } + } + + var tracker = module as NamespaceTracker; + if (tracker != null) { + object value = NamespaceTrackerOps.GetCustomMember(_projectState.CodeContext, tracker, index); + if (value != OperationFailed.Value) { + return value; + } else { + return this; + } + } + + object result; + if (_projectState.TryGetMember(module, index, showClr, out result)) { + return result; + } + } + return this; // sentinel indicating failure + } + + internal ISet GetClr(string index, bool showClr, ISet defaultValue) { + ISet result; + if (_variables.TryGetValue(index, out result)) { + return result ?? defaultValue; + } + + var attrs = new List(); + foreach (var module in _objects) { + try { + var attr = GetOne(module, index, showClr); + if (attr != this) { + attrs.Add(attr); + } + } catch { + // TODO: Remove when Python bug is fixed + } + } + + if (attrs.Count > 0) { + var ns = _projectState.GetNamespaceFromObjects(attrs); + result = _variables[index] = ns.SelfSet; + return result; + } else { + _variables[index] = null; + } + + return defaultValue; + } + + public object[] Objects { + get { return _objects; } + } + + #region IDictionary Members + + public void Add(string key, ISet value) { + throw new InvalidOperationException(); + } + + public bool ContainsKey(string key) { + throw new NotImplementedException(); + } + + public ICollection Keys { + get { + EnsureAll(); + List res = new List(_variables.Count); + foreach (var v in _variables) { + if (v.Value != null) { + res.Add(v.Key); + } + } + return res; + } + } + + private void EnsureAll() { + if (!_gotAll) { + HashSet result = new HashSet(); + if (_objects.Length == 1 && (_objects[0] is PythonType)) { + // fast path for when we're looking up in a type, this is about twice as fast + // as going through the normal code path. + PythonType pyType = (PythonType)_objects[0]; + foreach (var baseType in pyType.mro()) { + PythonType curType = (baseType as PythonType); + if (curType != null) { + var dict = new DictProxy(curType); + var enumerator = dict.iteritems(_showClr ? _projectState.CodeContextCls : _projectState.CodeContext); + while (enumerator.MoveNext()) { + PythonTuple value = (PythonTuple)enumerator.Current; + string key = (string)value[0]; + + if (_variables.ContainsKey(key)) { + continue; + } + + _variables[key] = _projectState.GetNamespaceFromObjects(value[1]).SelfSet; + } + } + } + } else { + foreach (var module in _objects) { + foreach (var name in Utils.DirHelper(module, _showClr)) { + GetClr(name, _showClr, null); + } + } + } + _gotAll = true; + } + } + + public bool Remove(string key) { + throw new InvalidOperationException(); + } + + public bool TryGetValue(string key, out ISet value) { + EnsureAll(); + return _variables.TryGetValue(key, out value) && value != null; + } + + public ICollection> Values { + get { throw new NotImplementedException(); } + } + + public ISet this[string index] { + get { + ISet result = GetValue(index, null); + if (result != null) { + return result; + } + if (Keys.Contains(index)) { + // work around bug where IronPython includes property methods + // in types overloaded by generic arity (e.g. + // System.Linq.Expressions.Expression) + return EmptySet.Instance; + } + throw new KeyNotFoundException(String.Format("Key {0} not found", index)); + } + set { + _variables[index] = value; + } + } + + #endregion + + #region ICollection>> Members + + public void Add(KeyValuePair> item) { + throw new InvalidOperationException(); + } + + public void Clear() { + throw new InvalidOperationException(); + } + + public bool Contains(KeyValuePair> item) { + throw new NotImplementedException(); + } + + public void CopyTo(KeyValuePair>[] array, int arrayIndex) { + throw new NotImplementedException(); + } + + public int Count { + get { return Keys.Count; } + } + + public bool IsReadOnly { + get { return true; } + } + + public bool Remove(KeyValuePair> item) { + throw new InvalidOperationException(); + } + + #endregion + + #region IEnumerable>> Members + + public IEnumerator>> GetEnumerator() { + EnsureAll(); + foreach (var v in _variables) { + if (v.Value != null) { + yield return v; + } + } + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() { + return GetEnumerator(); + } + + #endregion + + public ProjectState ProjectState { + get { + return _projectState; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ListAppendBoundBuiltinMethodInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ListAppendBoundBuiltinMethodInfo.cs new file mode 100644 index 0000000000..3f684d6700 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ListAppendBoundBuiltinMethodInfo.cs @@ -0,0 +1,42 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.PyAnalysis.Interpreter; +using IronPython.Compiler.Ast; + +namespace Microsoft.PyAnalysis.Values { + /// + /// Represents list.append on a list with known type information. + /// + class ListAppendBoundBuiltinMethodInfo : BoundBuiltinMethodInfo { + private readonly ListInfo _list; + + public ListAppendBoundBuiltinMethodInfo(ListInfo list, BuiltinMethodInfo method) + : base(method) { + _list = list; + } + + public override ISet Call(Node node, AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + if (args.Length == 1) { + _list.AppendItem(args[0]); + } + + return ProjectState._noneInst.SelfSet; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ListExtendBoundBuiltinFunction.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ListExtendBoundBuiltinFunction.cs new file mode 100644 index 0000000000..37bfbff1b0 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ListExtendBoundBuiltinFunction.cs @@ -0,0 +1,44 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.PyAnalysis.Interpreter; +using IronPython.Compiler.Ast; + +namespace Microsoft.PyAnalysis.Values { + /// + /// Represents list.extend on a list with known type information. + /// + class ListExtendBoundBuiltinMethodInfo : BoundBuiltinMethodInfo { + private readonly ListInfo _list; + + public ListExtendBoundBuiltinMethodInfo(ListInfo list, BuiltinMethodInfo method) + : base(method) { + _list = list; + } + + public override ISet Call(Node node, AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + if (args.Length == 1) { + foreach (var type in args[0]) { + _list.AppendItem(type.GetEnumeratorTypes(node, unit)); + } + } + + return ProjectState._noneInst.SelfSet; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ListInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ListInfo.cs new file mode 100644 index 0000000000..f0719a3fd2 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ListInfo.cs @@ -0,0 +1,100 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis.Values { + /// + /// Represents a list object with tracked type information. + /// + class ListInfo : SequenceInfo { + private ListAppendBoundBuiltinMethodInfo _appendMethod; + private ListPopBoundBuiltinMethodInfo _popMethod; + private ListInsertBoundBuiltinMethodInfo _insertMethod; + private ListExtendBoundBuiltinMethodInfo _extendMethod; + + public ListInfo(ISet[] indexTypes, BuiltinClassInfo seqType) + : base(indexTypes, seqType) { + EnsureAppend(); + } + + public override ISet GetMember(Node node, AnalysisUnit unit, string name) { + switch (name) { + case "append": + EnsureAppend(); + return _appendMethod.SelfSet; + case "pop": + EnsurePop(); + return _popMethod.SelfSet; + case "insert": + EnsureInsert(); + return _insertMethod.SelfSet; + case "extend": + EnsureExtend(); + return _extendMethod.SelfSet; + } + + return base.GetMember(node, unit, name); + } + + + internal void AppendItem(ISet set) { + ISet newTypes = set; + bool madeSet = false; + foreach (var type in IndexTypes) { + newTypes = newTypes.Union(type, ref madeSet); + } + + if (IndexTypes.Length != 1 || IndexTypes[0].Count != newTypes.Count) { + ReturnValue.EnqueueDependents(); + } + + UnionType = newTypes; + IndexTypes = new[] { newTypes }; + } + + private void EnsureAppend() { + if (_appendMethod == null) { + var appendMeth = VariableDict["append"]; + _appendMethod = new ListAppendBoundBuiltinMethodInfo(this, (BuiltinMethodInfo)appendMeth.First()); + } + } + + private void EnsurePop() { + if (_popMethod == null) { + var popMethod = VariableDict["pop"]; + _popMethod = new ListPopBoundBuiltinMethodInfo(this, (BuiltinMethodInfo)popMethod.First()); + } + } + + private void EnsureInsert() { + if (_insertMethod == null) { + var method = VariableDict["insert"]; + _insertMethod = new ListInsertBoundBuiltinMethodInfo(this, (BuiltinMethodInfo)method.First()); + } + } + + private void EnsureExtend() { + if (_extendMethod == null) { + var method = VariableDict["extend"]; + _extendMethod = new ListExtendBoundBuiltinMethodInfo(this, (BuiltinMethodInfo)method.First()); + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ListInsertBoundBuiltinFunction.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ListInsertBoundBuiltinFunction.cs new file mode 100644 index 0000000000..f6f4cbe3ea --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ListInsertBoundBuiltinFunction.cs @@ -0,0 +1,42 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.PyAnalysis.Interpreter; +using IronPython.Compiler.Ast; + +namespace Microsoft.PyAnalysis.Values { + /// + /// Represents list.insert on a list with known type information. + /// + class ListInsertBoundBuiltinMethodInfo : BoundBuiltinMethodInfo { + private readonly ListInfo _list; + + public ListInsertBoundBuiltinMethodInfo(ListInfo list, BuiltinMethodInfo method) + : base(method) { + _list = list; + } + + public override ISet Call(Node node, AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + if (args.Length == 2) { + _list.AppendItem(args[1]); + } + + return ProjectState._noneInst.SelfSet; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ListPopBoundBuiltinMethodInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ListPopBoundBuiltinMethodInfo.cs new file mode 100644 index 0000000000..314399a7bd --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ListPopBoundBuiltinMethodInfo.cs @@ -0,0 +1,38 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.PyAnalysis.Interpreter; +using IronPython.Compiler.Ast; + +namespace Microsoft.PyAnalysis.Values { + /// + /// Represents list.pop on a list with known type information. + /// + class ListPopBoundBuiltinMethodInfo : BoundBuiltinMethodInfo { + private readonly ListInfo _list; + + public ListPopBoundBuiltinMethodInfo(ListInfo list, BuiltinMethodInfo method) + : base(method) { + _list = list; + } + + public override ISet Call(Node node, AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + return _list.UnionType; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/MemberReferences.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/MemberReferences.cs new file mode 100644 index 0000000000..eebc23e368 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/MemberReferences.cs @@ -0,0 +1,102 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis.Values { + /// + /// Maintains a list of references keyed off of name. + /// + class MemberReferences { + private Dictionary _references; + + public void AddReference(Node node, AnalysisUnit unit, string name) { + if (!unit.ForEval) { + if (_references == null) { + _references = new Dictionary(); + } + ReferenceDict refs; + if (!_references.TryGetValue(name, out refs)) { + _references[name] = refs = new ReferenceDict(); + } + refs.GetReferences(unit.DeclaringModule.ProjectEntry).References.Add(new SimpleSrcLocation(node.Span)); + } + } + + public IEnumerable GetDefinitions(string name) { + ReferenceDict references; + if (_references != null && _references.TryGetValue(name, out references)) { + return references.Values; + } + return new IReferenceable[0]; + } + } + + /// + /// A collection of references which are keyd off of project entry. + /// + class ReferenceDict : Dictionary { + public ReferenceList GetReferences(ProjectEntry project) { + ReferenceList builtinRef; + if (!TryGetValue(project, out builtinRef) || builtinRef.Version != project.Version) { + this[project] = builtinRef = new ReferenceList(project); + } + return builtinRef; + } + + public IEnumerable AllReferences { + get { + foreach (var keyValue in this) { + foreach (var reference in keyValue.Value.References) { + yield return new LocationInfo(keyValue.Key, reference.Line, reference.Column, reference.Length); + } + } + } + } + } + + /// + /// A list of references as stored for a single project entry. + /// + class ReferenceList : IReferenceable { + public readonly int Version; + public readonly IProjectEntry Project; + public readonly HashSet References; + + public ReferenceList(IProjectEntry project) { + Version = project.Version; + Project = project; + References = new HashSet(); + } + + #region IReferenceable Members + + public IEnumerable> Definitions { + get { yield break; } + } + + IEnumerable> IReferenceable.References { + get { + foreach (var location in References) { + yield return new KeyValuePair(Project, location); + } + } + } + + #endregion + } + +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/MethodInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/MethodInfo.cs new file mode 100644 index 0000000000..04c2490360 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/MethodInfo.cs @@ -0,0 +1,116 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis.Values { + internal class MethodInfo : UserDefinedInfo { + private readonly FunctionInfo _function; + private readonly Namespace _instanceInfo; + + public MethodInfo(FunctionInfo function, Namespace instance) + : base(function._analysisUnit) { + _function = function; + _instanceInfo = instance; + } + + public override ISet Call(Node node, AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + return _function.Call(node, unit, Utils.Concat(_instanceInfo.SelfSet, args), keywordArgNames); + } + + public override IProjectEntry DeclaringModule { + get { + return _function.DeclaringModule; + } + } + + public override int DeclaringVersion { + get { + return _function.DeclaringVersion; + } + } + + public override LocationInfo Location { + get { + return _function.Location; + } + } + + public override string Description { + get { + var res = "method " + _function.FunctionDefinition.Name; + if (_instanceInfo is InstanceInfo) { + res += " of " + ((InstanceInfo)_instanceInfo).ClassInfo.ClassDefinition.Name + " objects "; + } + if (!String.IsNullOrEmpty(_function.FunctionDescription)) { + res += _function.FunctionDescription + Environment.NewLine; + } + return res; + } + } + + public override string ShortDescription { + get { + return Description;/* + var res = "method " + _function.FunctionDefinition.Name; + if (_instanceInfo is InstanceInfo) { + res += " of " + ((InstanceInfo)_instanceInfo).ClassInfo.ClassDefinition.Name + " objects" + Environment.NewLine; + } + return res;*/ + } + } + + public override ICollection Overloads { + get { + var p = _function.FunctionDefinition.Parameters; + var pp = new ParameterResult[p.Count - 1]; + for (int i = 1; i < p.Count; i++) { + pp[i - 1] = FunctionInfo.MakeParameterResult(_function.ProjectState, p[i]); + } + string doc; + if (_function.FunctionDefinition.Body != null) { + doc = _function.FunctionDefinition.Body.Documentation; + } else { + doc = String.Empty; + } + return new ReadOnlyCollection( + new OverloadResult[] { + new SimpleOverloadResult(pp, _function.FunctionDefinition.Name, doc) + } + ); + } + } + + public override string Documentation { + get { + return _function.Documentation; + } + } + + public override ResultType ResultType { + get { + return ResultType.Method; + } + } + + public override string ToString() { + var name = _function.FunctionDefinition.Name; + return "Method" /* + hex(id(self)) */ + " " + name; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ModuleInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ModuleInfo.cs new file mode 100644 index 0000000000..10fbc93253 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ModuleInfo.cs @@ -0,0 +1,168 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Text; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis.Values { + internal class ModuleInfo : Namespace, IReferenceableContainer { + private readonly string _name; + private readonly ProjectEntry _projectEntry; + private readonly Dictionary> _sequences; // sequences defined in the module + private readonly Dictionary _imports; // imports performed during the module + private readonly ModuleScope _scope; + private readonly WeakReference _weakModule; + private ModuleInfo _parentPackage; + public bool ShowClr { get; set; } + private DependentData _definition = new DependentData(); + + public ModuleInfo(string moduleName, ProjectEntry projectEntry) { + _name = moduleName; + _projectEntry = projectEntry; + ShowClr = false; + _sequences = new Dictionary>(); + _imports = new Dictionary(); + _scope = new ModuleScope(this); + _weakModule = new WeakReference(this); + } + + public override IDictionary> GetAllMembers(bool showClr) { + var res = new Dictionary>(); + foreach (var kvp in _scope.Variables) { + res[kvp.Key] = kvp.Value.Types; + } + return res; + } + + public ModuleInfo ParentPackage { + get { return _parentPackage; } + set { _parentPackage = value; } + } + + public override ISet GetMember(Node node, AnalysisUnit unit, string name) { + ModuleDefinition.AddDependency(unit); + + return Scope.CreateVariable(node, unit, name).Types; + } + + public override void SetMember(Node node, AnalysisUnit unit, string name, ISet value) { + var variable = Scope.CreateVariable(node, unit, name, false); + if (variable.AddTypes(node, unit, value)) { + ModuleDefinition.EnqueueDependents(); + } + + variable.AddAssignment(node, unit); + } + + /// + /// Gets a weak reference to this module + /// + public WeakReference WeakModule { + get { + return _weakModule; + } + } + + public DependentData ModuleDefinition { + get { + return _definition; + } + } + + public ModuleScope Scope { + get { + return _scope; + } + } + + public string Name { + get { return _name; } + } + + public ProjectEntry ProjectEntry { + get { return _projectEntry; } + } + + public Dictionary Imports { + get { + return _imports; + } + } + + public override ResultType ResultType { + get { + return ResultType.Module; + } + } + + public override string ToString() { + return "Module " + base.ToString(); + } + + public override string ShortDescription { + get { + return "Python module " + Name; + } + } + + public override string Description { + get { + var result = new StringBuilder("Python module "); + result.Append(Name); + var doc = ((PythonAst)ProjectEntry.Tree).Documentation; + if (doc != null) { + result.Append("\n\n"); + result.Append(doc); + } + return result.ToString(); + } + } + + public override LocationInfo Location { + get { + return new LocationInfo(ProjectEntry, 1, 1, 0); + } + } + + public Dictionary> NodeVariables { + get { return _sequences; } + } + + /// + /// Cached node variables so that we don't continually create new entries for basic nodes such + /// as sequences, lambdas, etc... + /// + public ISet GetOrMakeNodeVariable(Node node, Func> maker) { + ISet result; + if (!NodeVariables.TryGetValue(node, out result)) { + result = NodeVariables[node] = maker(node); + } + return result; + } + + #region IVariableDefContainer Members + + public IEnumerable GetDefinitions(string name) { + VariableDef def; + if (_scope.Variables.TryGetValue(name, out def)) { + yield return def; + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/Namespace.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/Namespace.cs new file mode 100644 index 0000000000..3302ed768a --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/Namespace.cs @@ -0,0 +1,376 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using IronPython.Compiler; +using IronPython.Compiler.Ast; +using IronPython.Runtime.Types; +using Microsoft.PyAnalysis.Interpreter; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; + +namespace Microsoft.PyAnalysis.Values { + /// + /// A namespace represents a set of variables and code. Examples of + /// namespaces include top-level code, classes, and functions. + /// + internal class Namespace : ISet, IAnalysisValue { + [ThreadStatic] private static HashSet _processing; + + public Namespace() { } + + /// + /// Returns an immutable set which contains just this Namespace. + /// + /// Currently implemented as returning the Namespace object directly which implements ISet{Namespace}. + /// + public ISet SelfSet { + get { return this; } + } + + #region Namespace Information + + public virtual LocationInfo Location { + get { return null; } + } + + private static OverloadResult[] EmptyOverloadResult = new OverloadResult[0]; + public virtual ICollection Overloads { + get { + return EmptyOverloadResult; + } + } + + public virtual string Documentation { + get { + return String.Empty; + } + } + + public virtual string Description { + get { return null; } + } + + public virtual string ShortDescription { + get { + return Description; + } + } + + public virtual ResultType ResultType { + get { return ResultType.Unknown; } + } + + public virtual bool IsBuiltin { + get { return false; } + } + + public virtual object GetConstantValue() { + return Type.Missing; + } + + public virtual IDictionary> GetAllMembers(bool showClr) { + return new Dictionary>(); + } + + public virtual PythonType PythonType { + get { return null; } + } + + public virtual IProjectEntry DeclaringModule { + get { + return null; + } + } + + public virtual int DeclaringVersion { + get { + return -1; + } + } + + #endregion + + #region Dynamic Operations + + /// + /// Attempts to call this object and returns the set of possible types it can return. + /// + /// The node which is triggering the call, for reference tracking + /// The analysis unit performing the analysis + /// The arguments being passed to the function + /// Keyword argument names, * and ** are included in here for splatting calls + public virtual ISet Call(Node node, AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + return EmptySet.Instance; + } + + public virtual ISet GetMember(Node node, AnalysisUnit unit, string name) { + return EmptySet.Instance; + } + + public virtual void SetMember(Node node, AnalysisUnit unit, string name, ISet value) { + } + + public virtual void DeleteMember(Node node, AnalysisUnit unit, string name) { + } + + public virtual void AugmentAssign(AugmentedAssignStatement node, AnalysisUnit unit, ISet value) { + } + + public virtual ISet BinaryOperation(Node node, AnalysisUnit unit, PythonOperator operation, ISet rhs) { + switch (operation) { + case PythonOperator.Is: + case PythonOperator.IsNot: + return unit.DeclaringModule.ProjectEntry.ProjectState._boolType.Instance; + } + return SelfSet.Union(rhs); + } + + public virtual ISet UnaryOperation(Node node, AnalysisUnit unit, PythonOperator operation) { + return this.SelfSet; + } + + /// + /// Returns the length of the object if it's known, or null if it's not a fixed size object. + /// + /// + public virtual int? GetLength() { + return null; + } + + public virtual ISet GetEnumeratorTypes(Node node, AnalysisUnit unit) { + // TODO: need more than constant 0... + //index = (VariableRef(ConstantInfo(0, self.ProjectState, False)), ) + //self.AssignTo(self._state.IndexInto(listRefs, index), node, node.Left) + return GetIndex(node, unit, unit.ProjectState._intType.SelfSet); + } + + public virtual ISet GetIndex(Node node, AnalysisUnit unit, ISet index) { + var item = GetMember(node, unit, "__getitem__"); + ISet result = EmptySet.Instance; + bool madeSet = false; + foreach (var ns in item) { + result = result.Union(ns.Call(node, unit, new[] { index }, ArrayUtils.EmptyStrings), ref madeSet); + } + return result; + } + + public virtual void SetIndex(Node node, AnalysisUnit unit, ISet index, ISet value) { + } + + public virtual ISet GetDescriptor(Namespace instance, AnalysisUnit unit) { + return SelfSet; + } + + public virtual ISet GetStaticDescriptor(AnalysisUnit unit) { + return SelfSet; + } + + #endregion + + #region Union Equality + + public virtual bool UnionEquals(Namespace ns) { + return Equals(ns); + } + + public virtual int UnionHashCode() { + return GetHashCode(); + } + + #endregion + + #region Recursion Tracking + + /// + /// Tracks whether or not we're currently processing this VariableRef to prevent + /// stack overflows. Returns true if the the variable should be processed. + /// + /// + public bool Push() { + if (_processing == null) { + _processing = new HashSet(); + } + + return _processing.Add(this); + } + + public void Pop() { + _processing.Remove(this); + } + + #endregion + + #region SelfSet + + #region ISet Members + + bool ISet.Add(Namespace item) { + throw new InvalidOperationException(); + } + + void ISet.ExceptWith(IEnumerable other) { + throw new InvalidOperationException(); + } + + void ISet.IntersectWith(IEnumerable other) { + throw new InvalidOperationException(); + } + + bool ISet.IsProperSubsetOf(IEnumerable other) { + throw new NotImplementedException(); + } + + bool ISet.IsProperSupersetOf(IEnumerable other) { + throw new NotImplementedException(); + } + + bool ISet.IsSubsetOf(IEnumerable other) { + throw new NotImplementedException(); + } + + bool ISet.IsSupersetOf(IEnumerable other) { + throw new NotImplementedException(); + } + + bool ISet.Overlaps(IEnumerable other) { + throw new NotImplementedException(); + } + + bool ISet.SetEquals(IEnumerable other) { + var enumerator = other.GetEnumerator(); + if (enumerator.MoveNext()) { + if (((ISet)this).Contains(enumerator.Current)) { + return !enumerator.MoveNext(); + } + } + return false; + } + + void ISet.SymmetricExceptWith(IEnumerable other) { + throw new InvalidOperationException(); + } + + void ISet.UnionWith(IEnumerable other) { + throw new InvalidOperationException(); + } + + #endregion + + #region ICollection Members + + void ICollection.Add(Namespace item) { + throw new NotImplementedException(); + } + + void ICollection.Clear() { + throw new InvalidOperationException(); + } + + bool ICollection.Contains(Namespace item) { + return EqualityComparer.Default.Equals(item, this); + } + + void ICollection.CopyTo(Namespace[] array, int arrayIndex) { + array[arrayIndex] = this; + } + + int ICollection.Count { + get { return 1; } + } + + bool ICollection.IsReadOnly { + get { return true; } + } + + bool ICollection.Remove(Namespace item) { + throw new InvalidOperationException(); + } + + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() { + return new SetOfOneEnumerator(this); + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + yield return this; + } + + #endregion + + class SetOfOneEnumerator : IEnumerator { + private readonly Namespace _value; + private bool _enumerated; + + public SetOfOneEnumerator(Namespace value) { + _value = value; + } + + #region IEnumerator Members + + Namespace IEnumerator.Current { + get { return _value; } + } + + #endregion + + #region IDisposable Members + + void IDisposable.Dispose() { + } + + #endregion + + #region IEnumerator Members + + object System.Collections.IEnumerator.Current { + get { return _value; } + } + + bool System.Collections.IEnumerator.MoveNext() { + if (_enumerated) { + return false; + } + _enumerated = true; + return true; + } + + void System.Collections.IEnumerator.Reset() { + _enumerated = false; + } + + #endregion + } + + #endregion + + internal virtual void AddReference(Node node, AnalysisUnit analysisUnit) { + } + + public virtual IEnumerable References { + get { + yield break; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/NamespaceReferences.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/NamespaceReferences.cs new file mode 100644 index 0000000000..74ace2d2f6 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/NamespaceReferences.cs @@ -0,0 +1,33 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + + +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using Microsoft.Scripting; + +namespace Microsoft.PyAnalysis.Values { + /// + /// Represents a collection of referneces for a namespace. The collection + /// version is the version of the referer. + /// + internal class NamespaceReferences { + public int Version { get; private set; } + public HashSet References { get; set; } + + public NamespaceReferences(int version) { + Version = version; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/NamespaceSetExtensions.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/NamespaceSetExtensions.cs new file mode 100644 index 0000000000..2f58cfd295 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/NamespaceSetExtensions.cs @@ -0,0 +1,255 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using IronPython.Compiler; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Interpreter; +using Microsoft.Scripting; + +namespace Microsoft.PyAnalysis.Values { + /// + /// Provides operations which can be performed in bulk over a set of namespaces which then result + /// in a new set of namespaces as the result. + /// + internal static class NamespaceSetExtensions { + /// + /// Performs a GetMember operation for the given name and returns the types of variables which + /// are associated with that name. + /// + public static ISet GetMember(this ISet self, Node node, AnalysisUnit unit, string name) { + ISet res = null; + // name can be empty if we have "foo." + if (name != null && name.Length > 0) { + bool madeSet = false; + foreach (var ns in self) { + ISet got = ns.GetMember(node, unit, name); + if (res == null) { + res = got; + continue; + } else if (!madeSet) { + res = new HashSet(res); + madeSet = true; + } + res.UnionWith(got); + } + } + return res ?? EmptySet.Instance; + } + + /// + /// Performs a SetMember operation for the given name and propagates the given values types + /// for the provided member name. + /// + public static void SetMember(this ISet self, Node node, AnalysisUnit unit, string name, ISet value) { + if (name != null && name.Length > 0) { + foreach (var ns in self) { + ns.SetMember(node, unit, name, value); + } + } + } + + /// + /// Performs a delete index operation propagating the index types into the provided object. + /// + public static void DeleteMember(this ISet self, Node node, AnalysisUnit unit, string name) { + if (name != null && name.Length > 0) { + foreach (var ns in self) { + ns.DeleteMember(node, unit, name); + } + } + } + + /// + /// Performs a call operation propagating the argument types into any user defined functions + /// or classes and returns the set of types which result from the call. + /// + public static ISet Call(this ISet self, Node node, AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + ISet res = EmptySet.Instance; + bool madeSet = false; + foreach (var ns in self) { + var call = ns.Call(node, unit, args, keywordArgNames); + Debug.Assert(call != null); + + res = res.Union(call, ref madeSet); + } + + return res; + } + + /// + /// Performs a get index operation propagating any index types into the value and returns + /// the associated types associated with the object. + /// + public static ISet GetIndex(this ISet self, Node node, AnalysisUnit unit, ISet index) { + ISet res = null; + bool madeSet = false; + foreach (var ns in self) { + ISet got = ns.GetIndex(node, unit, index); + if (res == null) { + res = got; + continue; + } else if (!madeSet) { + res = new HashSet(res); + madeSet = true; + } + res.UnionWith(got); + } + + return res ?? EmptySet.Instance; + } + + /// + /// Performs a set index operation propagating the index types and value types into + /// the provided object. + /// + public static void SetIndex(this ISet self, Node node, AnalysisUnit unit, ISet index, ISet value) { + foreach (var ns in self) { + ns.SetIndex(node, unit, index, value); + } + } + + /// + /// Performs a delete index operation propagating the index types into the provided object. + /// + public static void DeleteIndex(this ISet self, Node node, AnalysisUnit analysisState, ISet index) { + } + + /// + /// Performs an augmented assignment propagating the value into the object. + /// + public static void AugmentAssign(this ISet self, AugmentedAssignStatement node, AnalysisUnit unit, ISet value) { + foreach (var ns in self) { + ns.AugmentAssign(node, unit, value); + } + } + + /// + /// Returns the set of types which are accessible when code enumerates over the object + /// in a for statement. + /// + public static ISet GetEnumeratorTypes(this ISet self, Node node, AnalysisUnit unit) { + ISet res = null; + bool madeSet = false; + foreach (var ns in self) { + ISet got = ns.GetEnumeratorTypes(node, unit); + if (res == null) { + res = got; + continue; + } else if (!madeSet) { + res = new HashSet(res); + madeSet = true; + } + res.UnionWith(got); + } + + return res ?? EmptySet.Instance; + } + + /// + /// Performs a __get__ on the object. + /// + public static ISet GetDescriptor(this ISet self, Namespace instance, AnalysisUnit unit) { + ISet res = null; + bool madeSet = false; + foreach (var ns in self) { + ISet got = ns.GetDescriptor(instance, unit); + if (res == null) { + res = got; + continue; + } else if (!madeSet) { + res = new HashSet(res); + madeSet = true; + } + res.UnionWith(got); + } + + return res ?? EmptySet.Instance; + } + + /// + /// Performs a __get__ on the object when accessed from a class instead of an instance. + /// + public static ISet GetStaticDescriptor(this ISet self, AnalysisUnit unit) { + ISet res = null; + bool madeSet = false; + foreach (var ns in self) { + ISet got = ns.GetStaticDescriptor(unit); + if (res == null) { + res = got; + continue; + } else if (!madeSet) { + res = new HashSet(res); + madeSet = true; + } + res.UnionWith(got); + } + + return res ?? EmptySet.Instance; + } + + public static ISet BinaryOperation(this ISet self, Node node, AnalysisUnit unit, PythonOperator operation, ISet rhs) { + ISet res = null; + bool madeSet = false; + foreach (var ns in self) { + ISet got = ns.BinaryOperation(node, unit, operation, rhs); + if (res == null) { + res = got; + continue; + } else if (!madeSet) { + res = new HashSet(res); + madeSet = true; + } + res.UnionWith(got); + } + + return res ?? EmptySet.Instance; + } + + public static ISet UnaryOperation(this ISet self, Node node, AnalysisUnit unit, PythonOperator operation) { + ISet res = null; + bool madeSet = false; + foreach (var ns in self) { + ISet got = ns.UnaryOperation(node, unit, operation); + if (res == null) { + res = got; + continue; + } else if (!madeSet) { + res = new HashSet(res); + madeSet = true; + } + res.UnionWith(got); + } + + return res ?? EmptySet.Instance; + } + + public static Namespace GetUnionType(this ISet types) { + Namespace type = null; + if (types.Count == 1) { + type = System.Linq.Enumerable.First(types); + } else if (types.Count > 0) { + // simplify the types. + var set = new HashSet(types, TypeUnion.UnionComparer); + if (set.Count == 1) { + type = System.Linq.Enumerable.First(set); + } + } + return type; + } + + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/NumericInstanceInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/NumericInstanceInfo.cs new file mode 100644 index 0000000000..7979c7156c --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/NumericInstanceInfo.cs @@ -0,0 +1,31 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Interpreter; +using IronPython.Compiler; + +namespace Microsoft.PyAnalysis.Values { + class NumericInstanceInfo : BuiltinInstanceInfo { + + public NumericInstanceInfo(BuiltinClassInfo klass) + : base(klass) { + } + + public override ISet BinaryOperation(Node node, AnalysisUnit unit, PythonOperator operation, ISet rhs) { + switch (operation) { + case PythonOperator.GreaterThan: + case PythonOperator.LessThan: + case PythonOperator.LessThanOrEqual: + case PythonOperator.GreaterThanOrEqual: + case PythonOperator.Equal: + case PythonOperator.NotEqual: + case PythonOperator.Is: + case PythonOperator.IsNot: + return ProjectState._boolType.Instance; + } + return base.BinaryOperation(node, unit, operation, rhs); + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/RangeInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/RangeInfo.cs new file mode 100644 index 0000000000..3c245f0bc7 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/RangeInfo.cs @@ -0,0 +1,42 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using IronPython.Runtime.Types; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis.Values { + internal class RangeInfo : BuiltinInstanceInfo { + public RangeInfo(PythonType seqType, ProjectState state) + : base(state._listType) { + } + + public override ISet GetEnumeratorTypes(Node node, AnalysisUnit unit) { + return ProjectState._intType.SelfSet; + } + + public override ISet GetIndex(Node node, AnalysisUnit unit, ISet index) { + // TODO: Return correct index value if we have a constant + /*int? constIndex = SequenceInfo.GetConstantIndex(index); + + if (constIndex != null && constIndex.Value < _indexTypes.Count) { + // TODO: Warn if outside known index and no appends? + return _indexTypes[constIndex.Value]; + }*/ + + return ProjectState._intType.SelfSet; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ReflectedNamespace.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ReflectedNamespace.cs new file mode 100644 index 0000000000..f621ba8ded --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/ReflectedNamespace.cs @@ -0,0 +1,65 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Interpreter; +using Microsoft.Scripting.Actions; + +namespace Microsoft.PyAnalysis.Values { + /// + /// Represents a .NET namespace as exposed to Python + /// + internal class ReflectedNamespace : BuiltinNamespace, IReferenceableContainer { + private readonly MemberReferences _references = new MemberReferences(); + + public ReflectedNamespace(IEnumerable objects, ProjectState projectState) + : base(new LazyDotNetDict(objects, projectState, true)) { + } + + public override ISet GetMember(Node node, AnalysisUnit unit, string name) { + var res = base.GetMember(node, unit, name); + if (res.Count > 0) { + _references.AddReference(node, unit, name); + } + return res; + } + + public override IDictionary> GetAllMembers(bool showClr) { + return VariableDict; + } + + public override bool IsBuiltin { + get { return true; } + } + + public override ResultType ResultType { + get { + var modules = VariableDict.Objects; + if (modules.Length > 1 || modules[0] is NamespaceTracker) { + return ResultType.Namespace; + } + return ResultType.Field; + } + } + + #region IReferenceableContainer Members + + public IEnumerable GetDefinitions(string name) { + return _references.GetDefinitions(name); + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/SequenceInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/SequenceInfo.cs new file mode 100644 index 0000000000..c8afbd8849 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/SequenceInfo.cs @@ -0,0 +1,164 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Text; +using IronPython.Compiler.Ast; +using IronPython.Runtime.Types; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis.Values { + internal class SequenceInfo : BuiltinInstanceInfo { + private ISet _unionType; // all types that have been seen + private ISet[] _indexTypes; // types for known indices + private DependentData _returnValue; + + public SequenceInfo(ISet[] indexTypes, BuiltinClassInfo seqType) + : base(seqType) { + _indexTypes = indexTypes; + } + + public ISet[] IndexTypes { + get { return _indexTypes; } + protected set { _indexTypes = value; } + } + + public ISet UnionType { + get { + EnsureUnionType(); + return _unionType; + } + protected set { _unionType = value; } + } + + public override int? GetLength() { + return _indexTypes.Length; + } + + public override ISet GetEnumeratorTypes(Node node, AnalysisUnit unit) { + ReturnValue.AddDependency(unit); + + // TODO: This should be a union of the index types + if (_indexTypes.Length == 0) { + return EmptySet.Instance; + } + + return _indexTypes[0]; + } + + public DependentData ReturnValue { + get { + if (_returnValue == null) { + _returnValue = new DependentData(); + } + return _returnValue; + } + } + + public override ISet GetIndex(Node node, AnalysisUnit unit, ISet index) { + ReturnValue.AddDependency(unit); + int? constIndex = GetConstantIndex(index); + + if (constIndex != null && constIndex.Value < _indexTypes.Length) { + // TODO: Warn if outside known index and no appends? + return _indexTypes[constIndex.Value]; + } + + SliceInfo sliceInfo = GetSliceIndex(index); + if (sliceInfo != null) { + return this.SelfSet; + } + + EnsureUnionType(); + return _unionType; + } + + private SliceInfo GetSliceIndex(ISet index) { + foreach (var type in index) { + if (type is SliceInfo) { + return type as SliceInfo; + } + } + return null; + } + + private void EnsureUnionType() { + if (_unionType == null) { + ISet unionType = EmptySet.Instance; + bool setMade = false; + foreach (var set in _indexTypes) { + unionType = unionType.Union(set, ref setMade); + } + _unionType = unionType; + } + } + + internal static int? GetConstantIndex(ISet index) { + int? constIndex = null; + int typeCount = 0; + foreach (var type in index) { + object constValue = type.GetConstantValue(); + if (constValue != null && constValue is int) { + constIndex = (int)constValue; + } + + typeCount++; + } + if (typeCount != 1) { + constIndex = null; + } + return constIndex; + } + + public override string ShortDescription { + get { + return PythonType.Get__name__(_type); + } + } + + public override string Description { + get { + EnsureUnionType(); + StringBuilder result = new StringBuilder(PythonType.Get__name__(_type)); + var unionType = _unionType.GetUnionType(); + if (unionType != null) { + result.Append(" of " + unionType.ShortDescription); + } else { + result.Append("()"); + } + + return result.ToString(); + } + } + + public override bool IsBuiltin { + get { + return false; + } + } + + public override bool UnionEquals(Namespace ns) { + SequenceInfo si = ns as SequenceInfo; + if (si == null) { + return false; + } + + return si._indexTypes.Length == _indexTypes.Length; + } + + public override int UnionHashCode() { + return _indexTypes.Length; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/SetInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/SetInfo.cs new file mode 100644 index 0000000000..36e269c0cd --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/SetInfo.cs @@ -0,0 +1,72 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using IronPython.Runtime; +using IronPython.Runtime.Types; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis.Values { + internal class SetInfo : BuiltinInstanceInfo { + private readonly ISet _valueTypes; + + public SetInfo(HashSet valueTypes, ProjectState projectState, bool showClr) + : base(projectState._setType) { + _valueTypes = valueTypes; + } + + public override string ShortDescription { + get { + return "set"; + } + } + + public override string Description { + get { + // set({k}) + Namespace valueType = _valueTypes.GetUnionType(); + string valueName = valueType == null ? null : valueType.ShortDescription; + + if (valueName != null) { + return "{" + + (valueName ?? "unknown") + + "}"; + } + + return "set"; + } + } + + public override bool IsBuiltin { + get { + return false; + } + } + + public override bool UnionEquals(Namespace ns) { + return ns is DictionaryInfo; + } + + public override int UnionHashCode() { + return 2; + } + + public override ResultType ResultType { + get { + return ResultType.Field; + } + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/SliceInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/SliceInfo.cs new file mode 100644 index 0000000000..a6bbc64c3f --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/SliceInfo.cs @@ -0,0 +1,32 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.PyAnalysis.Values { + class SliceInfo : Namespace { + private ISet _start; + private ISet _stop; + private ISet _step; + + public SliceInfo(ISet start, ISet stop, ISet step) { + _start = start; + _stop = stop; + _step = step; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/SpecializedBuiltinFunction.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/SpecializedBuiltinFunction.cs new file mode 100644 index 0000000000..70e341df7e --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/SpecializedBuiltinFunction.cs @@ -0,0 +1,41 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using IronPython.Runtime.Types; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis.Values { + /// + /// Provides a built-in function whose analysis we deeply understand. Created with a custom delegate which + /// allows custom behavior rather than the typical behavior of returning the return type of the function. + /// + /// This is used for clr.AddReference* and calls to range() both of which we want to be customized in different + /// ways. + /// + class SpecializedBuiltinFunction : BuiltinFunctionInfo { + private readonly Func[], ISet> _call; + + public SpecializedBuiltinFunction(ProjectState state, BuiltinFunction function, Func[], ISet> call) + : base(function, state) { + _call = call; + } + + public override ISet Call(Node node, AnalysisUnit unit, ISet[] args, string[] keywordArgNames) { + return _call((CallExpression)node, unit, args) ?? base.Call(node, unit, args, keywordArgNames); + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/UnboundReference.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/UnboundReference.cs new file mode 100644 index 0000000000..7329712a0c --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/UnboundReference.cs @@ -0,0 +1,55 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +namespace Microsoft.PyAnalysis.Values { + internal class UnboundReferenceParameters { + private readonly UnboundReference _parent; + + public UnboundReferenceParameters(UnboundReference parent) { + _parent = parent; + } +#if FALSE + public VariableDef GetItem(int index) { + Debug.Assert(index >= 0); + _parent.EnsureParameters(index); + return _parent.Parameters[index]; + } +#endif + } + + internal class UnboundReference : Namespace { + private readonly string _name; + + public UnboundReference(string name) { + _name = name; + } +#if FALSE + private ISet[] _params; + + public void EnsureParameters(int size) { + if (_params == null) { + _params = new ISet[size + 1]; + } else if (size >= _params.Length) { + Array.Resize(ref _params, size + 1); + } + } + + public override ISet[] ParameterTypes { + get { + return _params; + } + } +#endif + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/UserDefinedInfo.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/UserDefinedInfo.cs new file mode 100644 index 0000000000..4c61381ea9 --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/UserDefinedInfo.cs @@ -0,0 +1,37 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using IronPython.Compiler.Ast; +using Microsoft.PyAnalysis.Interpreter; + +namespace Microsoft.PyAnalysis.Values { + internal class UserDefinedInfo : Namespace { + internal readonly AnalysisUnit _analysisUnit; + private VariableDef[] _parameters; + + protected UserDefinedInfo(AnalysisUnit analysisUnit) { + _analysisUnit = analysisUnit; + } + + public VariableDef[] ParameterTypes { + get { return _parameters; } + } + + public void SetParameters(VariableDef[] parameters) { + _parameters = parameters; + } + } +} diff --git a/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/Utils.cs b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/Utils.cs new file mode 100644 index 0000000000..f7114f6ddb --- /dev/null +++ b/Tools/IronStudio/IronPythonToolsCore/PyAnalysis/Values/Utils.cs @@ -0,0 +1,174 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.CompilerServices; +using System.Text; +using IronPython; +using IronPython.Compiler; +using IronPython.Runtime; +using IronPython.Runtime.Types; +using Microsoft.Scripting; +using Microsoft.Scripting.Actions; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.PyAnalysis.Values { + internal static class Utils { + internal static IList DirHelper(object obj, bool showClr) { + NamespaceTracker nt = obj as NamespaceTracker; + if (nt != null) { + return nt.GetMemberNames(); + } + + var dir = showClr ? ClrModule.DirClr(obj) : ClrModule.Dir(obj); + int len = dir.__len__(); + string[] result = new string[len]; + for (int i = 0; i < len; i++) { + // TODO: validate + result[i] = dir[i] as string; + } + return result; + } + + internal static List MakeList(object obj) { + var result = new List(); + result.Add(obj); + return result; + } + + internal static T[] RemoveFirst(this T[] array) { + if (array.Length < 1) { + return new T[0]; + } + T[] result = new T[array.Length - 1]; + Array.Copy(array, 1, result, 0, array.Length - 1); + return result; + } + + internal static string StripDocumentation(string doc) { + if (doc == null) { + return String.Empty; + } + StringBuilder result = new StringBuilder(doc.Length); + foreach (string line in doc.Split('\n')) { + if (result.Length > 0) { + result.Append("\r\n"); + } + result.Append(line.Trim()); + } + return result.ToString(); + } + + internal static string CleanDocumentation(string doc) { + int ctr = 0; + var result = new StringBuilder(doc.Length); + foreach (char c in doc) { + if (c == '\r') { + // pass + } else if (c == '\n') { + ctr++; + if (ctr < 3) { + result.Append("\r\n"); + } + } else { + result.Append(c); + ctr = 0; + } + } + return result.ToString().Trim(); + } + + internal static string GetDocumentation(ProjectState projectState, object obj) { + object doc; + if (!projectState.TryGetMember(obj, "__doc__", out doc)) { + return String.Empty; + } + return StripDocumentation(doc as string); + } + + internal static Parser CreateParser(SourceUnit sourceUnit, ErrorSink errorSink) { + return Parser.CreateParser( + new CompilerContext(sourceUnit, new PythonCompilerOptions(), errorSink), + new PythonOptions() + ); + } + + + internal static ISet GetReturnTypes(BuiltinFunction func, ProjectState projectState) { + var result = new HashSet(); + var found = new HashSet(); + foreach (var target in func.Overloads.Targets) { + var targetInfo = (target as System.Reflection.MethodInfo); + if (targetInfo != null && !found.Contains(targetInfo.ReturnType)) { + var pyType = ClrModule.GetPythonType(targetInfo.ReturnType); + result.Add(((BuiltinClassInfo)projectState.GetNamespaceFromObjects(pyType)).Instance); + found.Add(targetInfo.ReturnType); + } + } + return result; + } + + internal static T First(IEnumerable sequence) where T : class { + if (sequence == null) { + return null; + } + var enumerator = sequence.GetEnumerator(); + if (enumerator == null) { + return null; + } + try { + if (enumerator.MoveNext()) { + return enumerator.Current; + } else { + return null; + } + } finally { + enumerator.Dispose(); + } + } + + internal static T[] Concat(T firstArg, T[] args) { + var newArgs = new T[args.Length + 1]; + args.CopyTo(newArgs, 1); + newArgs[0] = firstArg; + return newArgs; + } + + internal static T Peek(this List stack) { + return stack[stack.Count - 1]; + } + + internal static void Push(this List stack, T value) { + stack.Add(value); + } + + internal static T Pop(this List stack) { + int pos = stack.Count - 1; + var result = stack[pos]; + stack.RemoveAt(pos); + return result; + } + } + + internal class ReferenceComparer : IEqualityComparer where T : class { + int IEqualityComparer.GetHashCode(T obj) { + return RuntimeHelpers.GetHashCode(obj); + } + + bool IEqualityComparer.Equals(T x, T y) { + return Object.ReferenceEquals(x, y); + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/GlobalSuppressions.cs b/Tools/IronStudio/IronRubyTools/GlobalSuppressions.cs new file mode 100644 index 0000000000..05e08b7adb --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/GlobalSuppressions.cs @@ -0,0 +1,25 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. Project-level +// suppressions either have no target or are given a specific target +// and scoped to a namespace, type, member, etc. +// +// To add a suppression to this file, right-click the message in the +// Error List, point to "Suppress Message(s)", and click "In Project +// Suppression File". You do not need to add suppressions to this +// file manually. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")] diff --git a/Tools/IronStudio/IronRubyTools/Guids.cs b/Tools/IronStudio/IronRubyTools/Guids.cs new file mode 100644 index 0000000000..18a0f48fcb --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Guids.cs @@ -0,0 +1,29 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +// Guids.cs +// MUST match guids.h +using System; + +namespace Microsoft.IronRubyTools +{ + static class GuidList + { + public const string guidIronRubyToolsPkgString = "65AC248D-B48B-40D1-83D8-FC82F98952A4"; + public const string guidIronRubyToolsCmdSetString = "F9682AFE-91B4-40FC-ABD2-7B1F67A52448"; + public const string guidToolWindowPersistanceString = "3F5345F7-B147-4B8D-A86D-0677855665B3"; + + public static readonly Guid guidIronRubyToolsCmdSet = new Guid(guidIronRubyToolsCmdSetString); + }; +} \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/Icons/ConsoleApplication.ico b/Tools/IronStudio/IronRubyTools/Icons/ConsoleApplication.ico new file mode 100644 index 0000000000..ae36f5cbe5 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/ConsoleApplication.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Icons/InteractiveWindow.bmp b/Tools/IronStudio/IronRubyTools/Icons/InteractiveWindow.bmp new file mode 100644 index 0000000000..11e05002c1 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/InteractiveWindow.bmp differ diff --git a/Tools/IronStudio/IronRubyTools/Icons/PackageFolder.bmp b/Tools/IronStudio/IronRubyTools/Icons/PackageFolder.bmp new file mode 100644 index 0000000000..cf3038c1b9 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/PackageFolder.bmp differ diff --git a/Tools/IronStudio/IronRubyTools/Icons/RakeFile.ico b/Tools/IronStudio/IronRubyTools/Icons/RakeFile.ico new file mode 100644 index 0000000000..9ad591eaa0 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/RakeFile.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Icons/RubyClassLibrary.ico b/Tools/IronStudio/IronRubyTools/Icons/RubyClassLibrary.ico new file mode 100644 index 0000000000..adcbde9bff Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/RubyClassLibrary.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Icons/RubyFile.bmp b/Tools/IronStudio/IronRubyTools/Icons/RubyFile.bmp new file mode 100644 index 0000000000..daf3ffef61 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/RubyFile.bmp differ diff --git a/Tools/IronStudio/IronRubyTools/Icons/RubyFile.ico b/Tools/IronStudio/IronRubyTools/Icons/RubyFile.ico new file mode 100644 index 0000000000..5933477959 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/RubyFile.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Icons/RubyGemsLibrary.ico b/Tools/IronStudio/IronRubyTools/Icons/RubyGemsLibrary.ico new file mode 100644 index 0000000000..cb20eefc3e Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/RubyGemsLibrary.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Icons/RubyTab.bmp b/Tools/IronStudio/IronRubyTools/Icons/RubyTab.bmp new file mode 100644 index 0000000000..b2939cac5e Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/RubyTab.bmp differ diff --git a/Tools/IronStudio/IronRubyTools/Icons/RubyWebsite.bmp b/Tools/IronStudio/IronRubyTools/Icons/RubyWebsite.bmp new file mode 100644 index 0000000000..ef6c8a859c Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/RubyWebsite.bmp differ diff --git a/Tools/IronStudio/IronRubyTools/Icons/RubyWebsite.ico b/Tools/IronStudio/IronRubyTools/Icons/RubyWebsite.ico new file mode 100644 index 0000000000..46098edc13 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/RubyWebsite.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Icons/SilverlightApplication.bmp b/Tools/IronStudio/IronRubyTools/Icons/SilverlightApplication.bmp new file mode 100644 index 0000000000..106eb6067a Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/SilverlightApplication.bmp differ diff --git a/Tools/IronStudio/IronRubyTools/Icons/SilverlightApplication.ico b/Tools/IronStudio/IronRubyTools/Icons/SilverlightApplication.ico new file mode 100644 index 0000000000..1a7ed68953 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/SilverlightApplication.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Icons/SilverlightLibraryApplication.ico b/Tools/IronStudio/IronRubyTools/Icons/SilverlightLibraryApplication.ico new file mode 100644 index 0000000000..6996cf83ed Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/SilverlightLibraryApplication.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Icons/WPFWindow.ico b/Tools/IronStudio/IronRubyTools/Icons/WPFWindow.ico new file mode 100644 index 0000000000..ec09635ffb Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/WPFWindow.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Icons/rbproj.bmp b/Tools/IronStudio/IronRubyTools/Icons/rbproj.bmp new file mode 100644 index 0000000000..cae0c49bae Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/rbproj.bmp differ diff --git a/Tools/IronStudio/IronRubyTools/Icons/rbproj.ico b/Tools/IronStudio/IronRubyTools/Icons/rbproj.ico new file mode 100644 index 0000000000..502c54400e Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Icons/rbproj.ico differ diff --git a/Tools/IronStudio/IronRubyTools/IronRuby Tools for VS License.rtf b/Tools/IronStudio/IronRubyTools/IronRuby Tools for VS License.rtf new file mode 100644 index 0000000000..421b2f05a3 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRuby Tools for VS License.rtf @@ -0,0 +1,607 @@ +{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff38\deff0\stshfdbch11\stshfloch0\stshfhich0\stshfbi0\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};} +{\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New{\*\falt Arial};}{\f3\fbidi \froman\fcharset2\fprq2{\*\panose 05050102010706020507}Symbol{\*\falt Bookshelf Symbol 3};} +{\f10\fbidi \fnil\fcharset2\fprq2{\*\panose 05000000000000000000}Wingdings{\*\falt Symbol};}{\f11\fbidi \fmodern\fcharset128\fprq1{\*\panose 02020609040205080304}MS Mincho{\*\falt ?l?r ??\'81\'66c};} +{\f11\fbidi \fmodern\fcharset128\fprq1{\*\panose 02020609040205080304}MS Mincho{\*\falt ?l?r ??\'81\'66c};}{\f38\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604030504040204}Tahoma{\*\falt ?l?r ??u!??I};} +{\f39\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0603020202020204}Trebuchet MS{\*\falt Univers};}{\f40\fbidi \fmodern\fcharset128\fprq1{\*\panose 02020609040205080304}@MS Mincho{\*\falt @MS Gothic};} +{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};}{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};} +{\fhimajor\f31502\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria;}{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};} +{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};}{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};} +{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};} +{\f42\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};}{\f43\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};}{\f45\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};} +{\f46\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};}{\f47\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};}{\f48\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};} +{\f49\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};}{\f50\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}{\f62\fbidi \fmodern\fcharset238\fprq1 Courier New CE{\*\falt Arial};} +{\f63\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr{\*\falt Arial};}{\f65\fbidi \fmodern\fcharset161\fprq1 Courier New Greek{\*\falt Arial};}{\f66\fbidi \fmodern\fcharset162\fprq1 Courier New Tur{\*\falt Arial};} +{\f67\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew){\*\falt Arial};}{\f68\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic){\*\falt Arial};}{\f69\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic{\*\falt Arial};} +{\f70\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese){\*\falt Arial};}{\f154\fbidi \fmodern\fcharset0\fprq1 MS Mincho Western{\*\falt ?l?r ??\'81\'66c};}{\f152\fbidi \fmodern\fcharset238\fprq1 MS Mincho CE{\*\falt ?l?r ??\'81\'66c};} +{\f153\fbidi \fmodern\fcharset204\fprq1 MS Mincho Cyr{\*\falt ?l?r ??\'81\'66c};}{\f155\fbidi \fmodern\fcharset161\fprq1 MS Mincho Greek{\*\falt ?l?r ??\'81\'66c};}{\f156\fbidi \fmodern\fcharset162\fprq1 MS Mincho Tur{\*\falt ?l?r ??\'81\'66c};} +{\f159\fbidi \fmodern\fcharset186\fprq1 MS Mincho Baltic{\*\falt ?l?r ??\'81\'66c};}{\f154\fbidi \fmodern\fcharset0\fprq1 MS Mincho Western{\*\falt ?l?r ??\'81\'66c};}{\f152\fbidi \fmodern\fcharset238\fprq1 MS Mincho CE{\*\falt ?l?r ??\'81\'66c};} +{\f153\fbidi \fmodern\fcharset204\fprq1 MS Mincho Cyr{\*\falt ?l?r ??\'81\'66c};}{\f155\fbidi \fmodern\fcharset161\fprq1 MS Mincho Greek{\*\falt ?l?r ??\'81\'66c};}{\f156\fbidi \fmodern\fcharset162\fprq1 MS Mincho Tur{\*\falt ?l?r ??\'81\'66c};} +{\f159\fbidi \fmodern\fcharset186\fprq1 MS Mincho Baltic{\*\falt ?l?r ??\'81\'66c};}{\f422\fbidi \fswiss\fcharset238\fprq2 Tahoma CE{\*\falt ?l?r ??u!??I};}{\f423\fbidi \fswiss\fcharset204\fprq2 Tahoma Cyr{\*\falt ?l?r ??u!??I};} +{\f425\fbidi \fswiss\fcharset161\fprq2 Tahoma Greek{\*\falt ?l?r ??u!??I};}{\f426\fbidi \fswiss\fcharset162\fprq2 Tahoma Tur{\*\falt ?l?r ??u!??I};}{\f427\fbidi \fswiss\fcharset177\fprq2 Tahoma (Hebrew){\*\falt ?l?r ??u!??I};} +{\f428\fbidi \fswiss\fcharset178\fprq2 Tahoma (Arabic){\*\falt ?l?r ??u!??I};}{\f429\fbidi \fswiss\fcharset186\fprq2 Tahoma Baltic{\*\falt ?l?r ??u!??I};}{\f430\fbidi \fswiss\fcharset163\fprq2 Tahoma (Vietnamese){\*\falt ?l?r ??u!??I};} +{\f431\fbidi \fswiss\fcharset222\fprq2 Tahoma (Thai){\*\falt ?l?r ??u!??I};}{\f432\fbidi \fswiss\fcharset238\fprq2 Trebuchet MS CE{\*\falt Univers};}{\f433\fbidi \fswiss\fcharset204\fprq2 Trebuchet MS Cyr{\*\falt Univers};} +{\f435\fbidi \fswiss\fcharset161\fprq2 Trebuchet MS Greek{\*\falt Univers};}{\f436\fbidi \fswiss\fcharset162\fprq2 Trebuchet MS Tur{\*\falt Univers};}{\f439\fbidi \fswiss\fcharset186\fprq2 Trebuchet MS Baltic{\*\falt Univers};} +{\f444\fbidi \fmodern\fcharset0\fprq1 @MS Mincho Western{\*\falt @MS Gothic};}{\f442\fbidi \fmodern\fcharset238\fprq1 @MS Mincho CE{\*\falt @MS Gothic};}{\f443\fbidi \fmodern\fcharset204\fprq1 @MS Mincho Cyr{\*\falt @MS Gothic};} +{\f445\fbidi \fmodern\fcharset161\fprq1 @MS Mincho Greek{\*\falt @MS Gothic};}{\f446\fbidi \fmodern\fcharset162\fprq1 @MS Mincho Tur{\*\falt @MS Gothic};}{\f449\fbidi \fmodern\fcharset186\fprq1 @MS Mincho Baltic{\*\falt @MS Gothic};} +{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};} +{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};} +{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};} +{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};} +{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};} +{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};} +{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};} +{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}{\fhimajor\f31528\fbidi \froman\fcharset238\fprq2 Cambria CE;} +{\fhimajor\f31529\fbidi \froman\fcharset204\fprq2 Cambria Cyr;}{\fhimajor\f31531\fbidi \froman\fcharset161\fprq2 Cambria Greek;}{\fhimajor\f31532\fbidi \froman\fcharset162\fprq2 Cambria Tur;} +{\fhimajor\f31535\fbidi \froman\fcharset186\fprq2 Cambria Baltic;}{\fhimajor\f31536\fbidi \froman\fcharset163\fprq2 Cambria (Vietnamese);}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};} +{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};} +{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};} +{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};} +{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};} +{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};} +{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};} +{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};} +{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};} +{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};} +{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};} +{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};} +{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} +{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;} +{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};} +{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};} +{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};}{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};} +{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255; +\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0; +\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;}{\*\defchp \fs22\dbch\af11 }{\*\defpap \ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 }\noqfpromote {\stylesheet{ +\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\snext0 \sautoupd \sqformat \spriority0 Normal;}{\s1\ql \fi-357\li357\ri0\sb120\sa120\widctlpar\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls17\outlinelevel0\adjustright\rin0\lin357\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 +\b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext1 \slink15 \sqformat heading 1;}{\s2\ql \fi-363\li720\ri0\sb120\sa120\widctlpar +\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl1\outlinelevel1\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext2 \slink16 \sqformat heading 2;}{\s3\ql \fi-357\li1077\ri0\sb120\sa120\widctlpar\tx1077\jclisttab\tx1440\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl2\outlinelevel2\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \af38\afs19\alang1025 +\ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext3 \slink17 \sqformat heading 3;}{\s4\ql \fi-358\li1435\ri0\sb120\sa120\widctlpar +\jclisttab\tx1437\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl3\outlinelevel3\adjustright\rin0\lin1435\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext4 \slink18 \sqformat heading 4;}{\s5\ql \fi-357\li1792\ri0\sb120\sa120\widctlpar\tx1792\jclisttab\tx2155\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl4\outlinelevel4\adjustright\rin0\lin1792\itap0 \rtlch\fcs1 \af38\afs19\alang1025 +\ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext5 \slink19 \sqformat heading 5;}{\s6\ql \fi-357\li2149\ri0\sb120\sa120\widctlpar +\jclisttab\tx2152\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl5\outlinelevel5\adjustright\rin0\lin2149\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext6 \slink20 \sqformat heading 6;}{\s7\ql \fi-357\li2506\ri0\sb120\sa120\widctlpar\jclisttab\tx2509\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl6\outlinelevel6\adjustright\rin0\lin2506\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext7 \slink21 \sqformat heading 7;}{\s8\ql \fi-357\li2863\ri0\sb120\sa120\widctlpar +\jclisttab\tx2866\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl7\outlinelevel7\adjustright\rin0\lin2863\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext8 \slink22 \sqformat heading 8;}{\s9\ql \fi-358\li3221\ri0\sb120\sa120\widctlpar\jclisttab\tx3223\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl8\outlinelevel8\adjustright\rin0\lin3221\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext9 \slink23 \sqformat heading 9;}{\*\cs10 \additive \ssemihidden Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\trcbpat1\trcfpat1\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f0\hich\af0\dbch\af11\cgrid\langnp1033\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}{\* +\cs15 \additive \rtlch\fcs1 \ab\af0\afs32 \ltrch\fcs0 \b\fs32\kerning32\loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink1 \slocked \spriority9 Heading 1 Char;}{\*\cs16 \additive \rtlch\fcs1 \ab\af38 \ltrch\fcs0 +\b\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\langnp1033\langfenp1033 \sbasedon10 \slink2 \slocked Heading 2 Char;}{\*\cs17 \additive \rtlch\fcs1 \ab\af0\afs26 \ltrch\fcs0 \b\fs26\loch\f31502\hich\af31502\dbch\af31501 +\sbasedon10 \slink3 \slocked \ssemihidden \spriority9 Heading 3 Char;}{\*\cs18 \additive \rtlch\fcs1 \ab\af0\afs28 \ltrch\fcs0 \b\fs28\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \slink4 \slocked \ssemihidden \spriority9 Heading 4 Char;}{\*\cs19 +\additive \rtlch\fcs1 \ab\ai\af0\afs26 \ltrch\fcs0 \b\i\fs26\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \slink5 \slocked \ssemihidden \spriority9 Heading 5 Char;}{\*\cs20 \additive \rtlch\fcs1 \ab\af0 \ltrch\fcs0 +\b\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \slink6 \slocked \ssemihidden \spriority9 Heading 6 Char;}{\*\cs21 \additive \rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs24\loch\f31506\hich\af31506\dbch\af31505 +\sbasedon10 \slink7 \slocked \ssemihidden \spriority9 Heading 7 Char;}{\*\cs22 \additive \rtlch\fcs1 \ai\af0\afs24 \ltrch\fcs0 \i\fs24\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \slink8 \slocked \ssemihidden \spriority9 Heading 8 Char;}{\*\cs23 +\additive \rtlch\fcs1 \af0 \ltrch\fcs0 \loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink9 \slocked \ssemihidden \spriority9 Heading 9 Char;}{\s24\ql \li357\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin357\itap0 +\rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext24 Body 1;}{ +\s25\ql \li720\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext25 Body 2;}{\s26\ql \li1077\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext26 Body 3;}{\s27\ql \li1435\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1435\itap0 \rtlch\fcs1 +\af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext27 Body 4;}{ +\s28\ql \li1803\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1803\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext28 Body 5;}{\s29\ql \li2160\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin2160\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext29 Body 6;}{\s30\ql \li2506\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin2506\itap0 \rtlch\fcs1 +\af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext30 Body 7;}{ +\s31\ql \li2863\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin2863\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext31 Body 8;}{\s32\ql \li3221\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin3221\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext32 Body 9;}{\s33\ql \fi-357\li357\ri0\sb120\sa120\widctlpar\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls1\adjustright\rin0\lin357\itap0 +\rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext33 Bullet 1;}{\s34\ql \fi-363\li720\ri0\sb120\sa120\widctlpar +\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext34 Bullet 2;}{ +\s35\ql \fi-357\li1077\ri0\sb120\sa120\widctlpar\jclisttab\tx1080\wrapdefault\aspalpha\aspnum\faauto\ls3\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext35 Bullet 3;}{\s36\ql \fi-358\li1435\ri0\sb120\sa120\widctlpar\jclisttab\tx1437\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin1435\itap0 +\rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext36 Bullet 4;}{\s37\ql \fi-357\li1792\ri0\sb120\sa120\widctlpar +\jclisttab\tx1795\wrapdefault\aspalpha\aspnum\faauto\ls5\adjustright\rin0\lin1792\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext37 Bullet 5;}{ +\s38\ql \fi-357\li2149\ri0\sb120\sa120\widctlpar\jclisttab\tx2152\wrapdefault\aspalpha\aspnum\faauto\ls6\adjustright\rin0\lin2149\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext38 Bullet 6;}{\s39\ql \fi-357\li2506\ri0\sb120\sa120\widctlpar\jclisttab\tx2509\wrapdefault\aspalpha\aspnum\faauto\ls7\adjustright\rin0\lin2506\itap0 +\rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext39 Bullet 7;}{\s40\ql \fi-357\li2863\ri0\sb120\sa120\widctlpar +\jclisttab\tx2866\wrapdefault\aspalpha\aspnum\faauto\ls8\adjustright\rin0\lin2863\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext40 Bullet 8;}{ +\s41\ql \fi-358\li3221\ri0\sb120\sa120\widctlpar\jclisttab\tx3223\wrapdefault\aspalpha\aspnum\faauto\ls9\adjustright\rin0\lin3221\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon32 \snext41 Bullet 9;}{\s42\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\ab\af38\afs28\alang1025 \ltrch\fcs0 \b\fs28\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 Heading EULA;}{\s43\ql \li0\ri0\sb120\sa120\widctlpar\brdrb\brdrs\brdrw10\brsp20 +\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs28\alang1025 \ltrch\fcs0 \b\fs28\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 Heading Software Title;}{ +\s44\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext44 Preamble;}{\s45\ql \li0\ri0\sb120\sa120\widctlpar\brdrb\brdrs\brdrw10\brsp20 \wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 +\b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext1 Preamble Border;}{\s46\qc \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext46 Heading Warranty;}{\s47\ql \fi-360\li360\ri0\sb120\sa120\widctlpar +\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls11\outlinelevel0\adjustright\rin0\lin360\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 +Heading 1 Warranty;}{\s48\ql \fi-360\li720\ri0\sb120\sa120\widctlpar\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls11\ilvl1\outlinelevel1\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 Heading 2 Warranty;}{\s49\ql \fi-357\li1077\ri0\sb120\sa120\widctlpar +\tx1077\jclisttab\tx1440\wrapdefault\aspalpha\aspnum\faauto\ls10\ilvl2\outlinelevel2\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon3 \snext49 Heading 3 Bold;}{\s50\ql \fi-358\li1435\ri0\sb120\sa120\widctlpar\jclisttab\tx1437\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin1435\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\ul\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon36 \snext50 Bullet 4 Underline;}{\s51\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\af38\afs19\alang1025 \ltrch\fcs0 \fs19\ul\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon35 \snext51 Bullet 3 Underline;}{ +\s52\ql \li720\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\ul\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon25 \snext52 Body 2 Underline;}{\s53\ql \li1077\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\ul\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon26 \snext53 Body 3 Underline;}{\s54\ql \li0\ri0\sb120\sa120\sl480\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 +\rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext54 \slink55 Body Text Indent;}{\*\cs55 \additive \rtlch\fcs1 \af38\afs19 \ltrch\fcs0 \f38\fs19 +\sbasedon10 \slink54 \slocked \ssemihidden Body Text Indent Char;}{\s56\ql \fi-358\li1435\ri0\sb120\sa120\widctlpar\jclisttab\tx1437\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin1435\itap0 \rtlch\fcs1 \ai\af38\afs19\alang1025 \ltrch\fcs0 +\i\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon36 \snext56 Bullet 4 Italics;}{\*\cs57 \additive \rtlch\fcs1 \af38 \ltrch\fcs0 \f38\lang1033\langfe1033\langnp1033\langfenp1033 \sbasedon10 Body 2 Char;}{\* +\cs58 \additive \rtlch\fcs1 \af38 \ltrch\fcs0 \f38\lang1033\langfe1033\langnp1033\langfenp1033 \sbasedon10 Body 3 Char;}{\*\cs59 \additive \rtlch\fcs1 \af38 \ltrch\fcs0 \f38\lang1033\langfe1033\langnp1033\langfenp1033 \sbasedon10 Body 4 Char;}{\*\cs60 +\additive \rtlch\fcs1 \af38 \ltrch\fcs0 \f38\lang1033\langfe1033\langnp1033\langfenp1033 \sbasedon10 Body 1 Char;}{\s61\ql \li0\ri0\sb120\sa120\widctlpar\brdrt\brdrs\brdrw10\brsp20 \wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 +\rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon44 \snext61 Preamble Border Above;}{ +\s62\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext62 \slink63 \ssemihidden footnote text;}{\*\cs63 \additive \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \f38\fs20 \sbasedon10 \slink62 \slocked \ssemihidden Footnote Text Char;}{\*\cs64 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \super +\sbasedon10 \ssemihidden footnote reference;}{\s65\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext65 \slink66 \ssemihidden endnote text;}{\*\cs66 \additive \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \f38\fs20 \sbasedon10 \slink65 \slocked \ssemihidden +Endnote Text Char;}{\*\cs67 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \super \sbasedon10 \ssemihidden endnote reference;}{\s68\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 +\ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext68 \slink69 \ssemihidden annotation text;}{\*\cs69 \additive \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \f38\fs20 +\sbasedon10 \slink68 \slocked \ssemihidden Comment Text Char;}{\*\cs70 \additive \rtlch\fcs1 \af0\afs16 \ltrch\fcs0 \fs16 \sbasedon10 \ssemihidden annotation reference;}{\s71\ql \li0\ri0\sa160\sl-240\slmult0 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext71 Char;}{ +\s72\ql \li0\ri0\sa160\sl-240\slmult0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext72 Char Char Char Char;}{\*\cs73 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf2 \sbasedon10 Hyperlink;}{\s74\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\af38\afs16\alang1025 \ltrch\fcs0 \fs16\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext74 \slink75 \ssemihidden Balloon Text;}{\*\cs75 \additive \rtlch\fcs1 \af38\afs16 \ltrch\fcs0 \f38\fs16 +\sbasedon10 \slink74 \slocked \ssemihidden Balloon Text Char;}{\*\cs76 \additive \rtlch\fcs1 \ab\af39 \ltrch\fcs0 \b\f39\lang1033\langfe1033\langnp1033\langfenp1033 \sbasedon10 Heading 2 Char1;}{\*\cs77 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \sbasedon10 +page number;}{\s78\ql \li0\ri0\sa160\sl-240\slmult0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext78 Char Char Char Char1;}{\s79\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 +\ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \snext0 \styrsid8999754 Body 0 Bold;}{\s80\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \snext0 \styrsid8999754 Body 0;}{\s81\ql \li0\ri0\sb120\sa120\widctlpar +\tqc\tx4320\tqr\tx8640\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext81 \slink82 \styrsid11496811 header;}{\*\cs82 \additive \rtlch\fcs1 \af38\afs19 \ltrch\fcs0 \f38\fs19 \sbasedon10 \slink81 \slocked \ssemihidden Header Char;}{\s83\ql \li0\ri0\sb120\sa120\widctlpar +\tqc\tx4320\tqr\tx8640\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext83 \slink84 \styrsid11496811 footer;}{\*\cs84 \additive \rtlch\fcs1 \af38\afs19 \ltrch\fcs0 \f38\fs19 \sbasedon10 \slink83 \slocked \ssemihidden Footer Char;}{ +\s85\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs20\alang1025 \ltrch\fcs0 \b\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon68 \snext68 \slink86 \ssemihidden \sunhideused \styrsid8850911 annotation subject;}{\*\cs86 \additive \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\f38\fs20 \sbasedon69 \slink85 \slocked \ssemihidden \styrsid8850911 Comment Subject Char;}} +{\*\listtable{\list\listtemplateid176468498\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid692200086\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s41\fi-358\li3221 +\jclisttab\tx3223\lin3221 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23 +\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691 +\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;} +\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid196815738}{\list\listtemplateid186961718{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers +\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2160 +\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2880\jclisttab\tx2880\lin2880 } +{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc4 +\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc4\levelnfcn4\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\lvltentative\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\lvltentative +\levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid259719883}{\list\listtemplateid-1793664660{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af0 \ltrch\fcs0 \b\i0\fbias0 \s47\fi-360\li360\jclisttab\tx360\lin360 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af0 \ltrch\fcs0 \b\i0\fbias0 \s48\fi-360\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'02\'02);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li1080\jclisttab\tx1080\lin1080 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'03(\'03);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'03(\'04);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li1800\jclisttab\tx1800\lin1800 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'05);}{\levelnumbers +\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 +\ltrch\fcs0 \fbias0 \fi-360\li2520\jclisttab\tx2520\lin2520 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 +\fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li3240 +\jclisttab\tx3240\lin3240 }{\listname ;}\listid394402059}{\list\listtemplateid1928476992{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0 \s49\fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers +\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'02i.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'02A.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid398796681}{\list\listtemplateid789093748\listhybrid{\listlevel\levelnfc23\levelnfcn23 +\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid-317712510\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s34\fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691 +\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;} +\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040 +\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid477573462} +{\list\listtemplateid1948578256{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-357\li357 +\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af0\afs20 \ltrch\fcs0 \b\i0\fs20\fbias0 \fi-360\li720 +\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li1077 +\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 +\fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 +\b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid630479929}{\list\listtemplateid67698717{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'02\'00);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li360\jclisttab\tx360\lin360 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'02\'01);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02);}{\levelnumbers\'01;}\rtlch\fcs1 +\af0 \ltrch\fcs0 \fi-360\li1080\jclisttab\tx1080\lin1080 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'03);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li1440 +\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'04);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li1800\jclisttab\tx1800\lin1800 }{\listlevel +\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'05);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2520\jclisttab\tx2520\lin2520 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li3240\jclisttab\tx3240\lin3240 }{\listname ;}\listid700712945}{\list\listtemplateid-53848358{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0 \s1\fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0 \s2\fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0 \s3\fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \s4\fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \s5\fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \s6\fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4 +\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \s7\fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255 +\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \s8\fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255 +\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \s9\fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname +;}\listid752163927}{\list\listtemplateid2088029282{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 +\b\i0\f39\fs20\fbias0 \fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 +\b\i0\f39\fs20\fbias0 \fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 +\b\i0\f38\fs20\fbias0 \fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 +\b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers +\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers +\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers +;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;} +\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid800729109}{\list\listtemplateid-296591990\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s40\fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160 +\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760 +\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname +;}\listid810947713}{\list\listtemplateid1567531878{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 +\b\i0\f39\fs20\fbias0 \fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 +\b\i0\f39\fs20\fbias0 \fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 +\b0\i0\f39\fs20\fbias0 \fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 +\b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers +\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers +\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers +;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;} +\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid826823576}{\list\listtemplateid2088029282{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0 \fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid974869818} +{\list\listtemplateid-1813845996\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s39\fi-357\li2506\jclisttab\tx2509\lin2506 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 +\fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1219436735}{\list\listtemplateid-41362566\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s36\fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;} +\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 +\fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1559511898}{\list\listtemplateid-743794326\listhybrid +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid2033377338\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s35\fi-357\li1077\jclisttab\tx1080\lin1077 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;} +\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760 +\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 } +{\listname ;}\listid1567649130}{\list\listtemplateid1363474438\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid-1175557160\'01\u-3913 ?;}{\levelnumbers;} +\f3\fbias0 \s37\fi-357\li1792\jclisttab\tx1795\lin1792 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440 +\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace1077\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0 +{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1848404271}{\list\listtemplateid-1802592190\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\leveltemplateid1229593488\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s38\fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;} +\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600 +\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1877695764}{\list\listtemplateid1186249844\listhybrid{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid1637229796\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s33\fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc23\levelnfcn23 +\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;} +\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760 +\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 } +{\listname ;}\listid2054619191}}{\*\listoverridetable{\listoverride\listid2054619191\listoverridecount0\ls1}{\listoverride\listid477573462\listoverridecount0\ls2}{\listoverride\listid1567649130\listoverridecount0\ls3}{\listoverride\listid1559511898 +\listoverridecount0\ls4}{\listoverride\listid1848404271\listoverridecount0\ls5}{\listoverride\listid1877695764\listoverridecount0\ls6}{\listoverride\listid1219436735\listoverridecount0\ls7}{\listoverride\listid810947713\listoverridecount0\ls8} +{\listoverride\listid196815738\listoverridecount0\ls9}{\listoverride\listid398796681\listoverridecount0\ls10}{\listoverride\listid394402059\listoverridecount0\ls11}{\listoverride\listid700712945\listoverridecount0\ls12}{\listoverride\listid826823576 +\listoverridecount0\ls13}{\listoverride\listid630479929\listoverridecount0\ls14}{\listoverride\listid800729109\listoverridecount0\ls15}{\listoverride\listid974869818\listoverridecount0\ls16}{\listoverride\listid752163927\listoverridecount0\ls17} +{\listoverride\listid398796681\listoverridecount0\ls18}{\listoverride\listid398796681\listoverridecount0\ls19}{\listoverride\listid477573462\listoverridecount0\ls20}{\listoverride\listid259719883\listoverridecount0\ls21}}{\*\pgptbl {\pgp\ipgp44\itap0\li0 +\ri0\sb0\sa0}{\pgp\ipgp37\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp26\itap0\li-225\ri0\sb0\sa0}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp43\itap0\li-225\ri0\sb0\sa0}{\pgp\ipgp20\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp29\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp24\itap0\li0\ri0 +\sb0\sa0}{\pgp\ipgp41\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp34\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp33\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp6\itap0\li-225\ri0\sb0\sa0}{\pgp\ipgp28\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp27\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp42\itap0\li0\ri0\sb0\sa0 +}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp39\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp8\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp21\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp22\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp1\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp11\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp2 +\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp10\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp48\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp23\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp31\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp32\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp5\itap0\li0 +\ri0\sb0\sa0}{\pgp\ipgp12\itap0\li-225\ri0\sb0\sa0}{\pgp\ipgp45\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp16\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp40\itap0\li-225\ri0\sb0\sa0}{\pgp\ipgp3\itap0\li-225\ri0\sb0\sa0}{\pgp\ipgp9\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp15\itap0\li0 +\ri0\sb0\sa0}{\pgp\ipgp14\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp46\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp36\itap0\li-225\ri0\sb0\sa0}{\pgp\ipgp25\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp0\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp19\itap0\li-225\ri0\sb0\sa0}{\pgp\ipgp7\itap0\li0\ri0 +\sb0\sa0}{\pgp\ipgp35\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp30\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp38\itap0\li0\ri0\sb0\sa0}{\pgp\ipgp4\itap0\li0\ri0\sb0\sa0}}{\*\rsidtbl \rsid17701\rsid72953\rsid200783\rsid222748\rsid345491\rsid480810\rsid535495\rsid538148 +\rsid555183\rsid663661\rsid676065\rsid745150\rsid787759\rsid986785\rsid1006099\rsid1009112\rsid1012355\rsid1070219\rsid1122066\rsid1182701\rsid1206375\rsid1245853\rsid1342505\rsid1512944\rsid1528414\rsid1529837\rsid1591306\rsid1722062\rsid1790012 +\rsid1800865\rsid1845488\rsid1901753\rsid1987218\rsid2173756\rsid2239916\rsid2571685\rsid2584538\rsid2765700\rsid2775782\rsid2781980\rsid2784514\rsid2818569\rsid2830425\rsid2962852\rsid3042060\rsid3162620\rsid3163049\rsid3370445\rsid3411320\rsid3411753 +\rsid3416253\rsid3439038\rsid3475551\rsid3611186\rsid3689565\rsid3739474\rsid3806252\rsid3822783\rsid4022155\rsid4023230\rsid4144829\rsid4202022\rsid4259872\rsid4287357\rsid4287841\rsid4457576\rsid4595328\rsid4738534\rsid4739523\rsid4793230\rsid4814690 +\rsid4865423\rsid4878548\rsid4995346\rsid5010248\rsid5062678\rsid5140435\rsid5185544\rsid5250241\rsid5405299\rsid5450553\rsid5459775\rsid5519492\rsid5525537\rsid5647101\rsid5660926\rsid5718733\rsid5718961\rsid5773282\rsid5788093\rsid5901771\rsid6033147 +\rsid6042923\rsid6119652\rsid6184270\rsid6227403\rsid6231754\rsid6304161\rsid6365404\rsid6373957\rsid6425843\rsid6453852\rsid6492030\rsid6498245\rsid6500924\rsid6506467\rsid6647886\rsid6758513\rsid6888647\rsid6889714\rsid6954571\rsid6971210\rsid7028642 +\rsid7100767\rsid7226971\rsid7282236\rsid7285345\rsid7290457\rsid7345747\rsid7428746\rsid7433926\rsid7436632\rsid7438204\rsid7495929\rsid7554964\rsid7619174\rsid7692510\rsid7754893\rsid7800249\rsid7878867\rsid8004214\rsid8132403\rsid8197303\rsid8214982 +\rsid8259998\rsid8324055\rsid8325040\rsid8419363\rsid8458805\rsid8545132\rsid8671477\rsid8679719\rsid8738620\rsid8745808\rsid8812012\rsid8850911\rsid8857738\rsid8858237\rsid8999754\rsid9071447\rsid9203972\rsid9261549\rsid9307880\rsid9321702\rsid9526807 +\rsid9649378\rsid9651656\rsid9664357\rsid9703728\rsid9778027\rsid9796909\rsid9857610\rsid9860938\rsid9861873\rsid9964378\rsid10171790\rsid10179250\rsid10294454\rsid10749433\rsid10813938\rsid10882308\rsid10955317\rsid11084414\rsid11142543\rsid11347136 +\rsid11432977\rsid11496811\rsid11498068\rsid11622420\rsid11672016\rsid11686297\rsid11695497\rsid11754382\rsid11874088\rsid11882246\rsid11937164\rsid12000701\rsid12015935\rsid12020896\rsid12065226\rsid12199483\rsid12222130\rsid12255436\rsid12407360 +\rsid12585274\rsid12596065\rsid12664082\rsid12722678\rsid12797652\rsid12798176\rsid12913505\rsid13136677\rsid13309404\rsid13334496\rsid13388123\rsid13828315\rsid13832939\rsid13896616\rsid13908819\rsid13965668\rsid14168694\rsid14223456\rsid14293847 +\rsid14297853\rsid14380549\rsid14382435\rsid14443673\rsid14491415\rsid14705568\rsid14771509\rsid14825379\rsid14830971\rsid14889524\rsid14894057\rsid14897950\rsid14943232\rsid15007790\rsid15427736\rsid15493712\rsid15495555\rsid15545976\rsid15601712 +\rsid15811431\rsid15822672\rsid15872081\rsid15925451\rsid16141742\rsid16202142\rsid16385696\rsid16395859\rsid16406536\rsid16450365\rsid16542934\rsid16661796\rsid16712132\rsid16716683}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1 +\mlMargin0\mrMargin0\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\title MICROSOFT [SoftwareType IS "Beta Software"=PRE-RELEASE ][SoftwareType IS "Evaluation Software"=EVALUATION ]SOFTWARE LICENSE TERMS}{\creatim\yr2010\mo2\dy4\hr11\min52} +{\revtim\yr2010\mo8\dy26\hr17\min13}{\version1}{\edmins0}{\nofpages4}{\nofwords1392}{\nofchars7936}{\nofcharsws9310}{\vern49247}}{\*\userprops {\propname db_build_version}\proptype30{\staticval 2.6}{\propname db_charger_document_reference}\proptype3 +{\staticval 25146}{\propname db_charger_client_name}\proptype30{\staticval tbc}{\propname db_charger_matter_number}\proptype30{\staticval tbc}{\propname autosave}\proptype30{\staticval false}{\propname owner}\proptype30{\staticval REDMOND\'5ckathan} +{\propname db_master_reference}\proptype30{\staticval USETERMS_MAINB}{\propname db_master_version}\proptype30{\staticval 20081001}{\propname db_master_clock}\proptype3{\staticval 783}{\propname db_master_name}\proptype30{\staticval Retail/OEM Software Lic +ense Terms - Main}{\propname db_master_description}\proptype30{\staticval }{\propname db_output_filter_reference}\proptype30{\staticval }{\propname db_base_url}\proptype30{\staticval http://usetermassembly/dealbuilder_live/DealBuilderNET/dealbuilder.asp +x}{\propname ProductVersion}\proptype30{\staticval 0}{\propname MScom}\proptype11{\staticval 0}{\propname LanguageAll}\proptype30{\staticval English}{\propname CanadaAvail}\proptype11{\staticval 1}{\propname CanadaFrench}\proptype11{\staticval 0} +{\propname FileFormat}\proptype11{\staticval 0}{\propname SoftwareType}\proptype30{\staticval Beta Software}{\propname ProductName}\proptype30{\staticval Incubation Software}{\propname NumberOfCopies}\proptype30{\staticval Any number of copies} +{\propname MandatoryActivation}\proptype11{\staticval 0}{\propname BetaUseRight}\proptype30{\staticval On the user's premises}{\propname ProductKey}\proptype11{\staticval 0}{\propname ConfidentialInformation}\proptype11{\staticval 0}{\propname Feedback} +\proptype30{\staticval Optional}{\propname NetFramework}\proptype11{\staticval 0}{\propname InternetBasedServices}\proptype11{\staticval 0}{\propname InternetBasedServicesFeaturesDescOther}\proptype30{\staticval }{\propname TimeBomb}\proptype11 +{\staticval 0}{\propname TermWhen}\proptype30{\staticval A certain number of years}{\propname TermYears}\proptype30{\staticval Two}{\propname TermCommRel}\proptype11{\staticval 0}{\propname db_commit}\proptype30{\staticval ProductVersion}} +{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl1440\margr1440\margt1440\margb1440\gutter0\ltrsect +\widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1\noxlattoyen +\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\hyphcaps0\formshade\horzdoc\dgmargin\dghspace95\dgvspace180\dghorigin1440\dgvorigin1440\dghshow2\dgvshow1 +\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\rempersonalinfo\rsidroot10813938 \fet0{\*\wgrffmtfilter 013f}\ilfomacatclnup0 +{\*\docvar {db_xml}{\'0d\'0dhttp://usetermassembly/dealbuilder_live/DealBuilderNET/dealbuilder.aspxmicrosoftmicrosoftmicrosoft25145tbctbcUSETERMS_MAINB2.6Retail/OEM Software License Terms - Main 20081001783trueuniquetrue +truetruetruetruetruetruelazyday_month_year,.day_month_year,._blank +rtffalsedraftingindefinitetrueautosave|text|falseowner|text|REDMOND +\'5ckathantruefalsetruepromptvaluepagegroup< +Value>sureunsureunknowndeferredfalsealiasfalseascendingfalsetruefalseRepeat< +Control NAME="db_input_heading_highlight_column" TYPE="string">CheckPrompt +AnswerDeferralGuidanceInsert your comments belowVariable/dealbuilder_live/help/dealb +uilder/help.htmlonsubmittruetruefalsetruefalsefalsetrue2dropdownsureUnknownfirstOtherlast20204Specify others:Specify other:11, and and/or or YesNo< +/Control>(%1 of %2)&\'3bnbsp\'3bvisibledigitsPrevNext&\'3bnbsp\'3b|&\'3bnbsp\'3b*aftera +ftertruefalseclient_side<\'3bU>\'3bWARNING:<\'3b/U>\'3b That page is no longer relevant because of answers given on this page or a previous page!enabledrelevant_pages0English100Beta SoftwareIncubation SoftwareAny number of copies0On the user&apos\'3bs premises00Optional000A certain number of yearsTwo0}}{\*\ftnsep \ltrpar \pard\plain \ltrpar +\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 +\ltrch\fcs0 \insrsid5647101 \chftnsep +\par }}{\*\ftnsepc \ltrpar \pard\plain \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid5647101 \chftnsepc +\par }}{\*\aftnsep \ltrpar \pard\plain \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid5647101 \chftnsep +\par }}{\*\aftnsepc \ltrpar \pard\plain \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid5647101 \chftnsepc +\par }}\ltrpar \sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4 +\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (} +{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar +\qc \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7436632 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 Apache License\line Version 2.0, January 2004\line } +{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 HYPERLINK "http://www.apache.org/licenses/" }{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 +\f0\fs24\insrsid7436632\charrsid12260020 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b5800000068007400740070003a002f002f007700770077002e006100700061006300680065002e006f00720067002f006c006900630065006e007300650073002f000000795881f43b1d7f48af2c825dc48527630000 +0000a5ab00003c}}}{\fldrslt {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\ul\cf2\insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 http://www.apache.org/licenses/}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 +\f0\fs24\insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 +\par }\pard \ltrpar\ql \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7436632 {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 +TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION +\par }{\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7436632\charrsid12260020 {\*\bkmkstart definitions}\hich\af0\dbch\af11\loch\f0 1. Definitions}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid7436632\charrsid12260020 {\*\bkmkend definitions} +. +\par \hich\af0\dbch\af11\loch\f0 "License" shall mean the terms and conditions for use, reproduction, and distribution as defined by Sections 1 through 9 of this document. +\par \hich\af0\dbch\af11\loch\f0 "Licensor" shall mean the copyright owner or entity authorized by the copyright owner th\hich\af0\dbch\af11\loch\f0 at is granting the License. +\par \hich\af0\dbch\af11\loch\f0 "Legal Entity" shall mean the union of the acting entity and all other entities that control, are controlled by, or are under common control with that entity. For the purposes of this definition, "control" means (i) the power, +\hich\af0\dbch\af11\loch\f0 +direct or indirect, to cause the direction or management of such entity, whether by contract or otherwise, or (ii) ownership of fifty percent (50%) or more of the outstanding shares, or (iii) beneficial ownership of such entity. +\par \hich\af0\dbch\af11\loch\f0 "You" (or "Your") shall me\hich\af0\dbch\af11\loch\f0 an an individual or Legal Entity exercising permissions granted by this License. +\par \hich\af0\dbch\af11\loch\f0 "Source" form shall mean the preferred form for making modifications, including but not limited to software source code, documentation source, and configuration files. +\par \hich\af0\dbch\af11\loch\f0 "Obje\hich\af0\dbch\af11\loch\f0 +ct" form shall mean any form resulting from mechanical transformation or translation of a Source form, including but not limited to compiled object code, generated documentation, and conversions to other media types. +\par \hich\af0\dbch\af11\loch\f0 "Work" shall mean the work of authorsh\hich\af0\dbch\af11\loch\f0 +ip, whether in Source or Object form, made available under the License, as indicated by a copyright notice that is included in or attached to the work (an example is provided in the Appendix below). +\par \hich\af0\dbch\af11\loch\f0 "Derivative Works" shall mean any work, whether in Sourc\hich\af0\dbch\af11\loch\f0 +e or Object form, that is based on (or derived from) the Work and for which the editorial revisions, annotations, elaborations, or other modifications represent, as a whole, an original work of authorship. For the purposes of this License, Derivative Work +\hich\af0\dbch\af11\loch\f0 s\hich\af0\dbch\af11\loch\f0 shall not include works that remain separable from, or merely link (or bind by name) to the interfaces of, the Work and Derivative Works thereof. +\par \hich\af0\dbch\af11\loch\f0 "Contribution" shall mean any work of authorship, including the original version of the Work and any modific\hich\af0\dbch\af11\loch\f0 +ations or additions to that Work or Derivative Works thereof, that is intentionally submitted to Licensor for inclusion in the Work by the copyright owner or by an individual or Legal Entity authorized to submit on behalf of the copyright owner. For the p +\hich\af0\dbch\af11\loch\f0 u\hich\af0\dbch\af11\loch\f0 +rposes of this definition, "submitted" means any form of electronic, verbal, or written communication sent to the Licensor or its representatives, including but not limited to communication on electronic mailing lists, source code control systems, and iss +\hich\af0\dbch\af11\loch\f0 u\hich\af0\dbch\af11\loch\f0 +e tracking systems that are managed by, or on behalf of, the Licensor for the purpose of discussing and improving the Work, but excluding communication that is conspicuously marked or otherwise designated in writing by the copyright owner as "Not a Contri +\hich\af0\dbch\af11\loch\f0 b\hich\af0\dbch\af11\loch\f0 ution." +\par \hich\af0\dbch\af11\loch\f0 "Contributor" shall mean Licensor and any individual or Legal Entity on behalf of whom a Contribution has been received by Licensor and subsequently incorporated within the Work. +\par }{\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7436632\charrsid12260020 {\*\bkmkstart copyright}\hich\af0\dbch\af11\loch\f0 2. Grant of Copyright License}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid7436632\charrsid12260020 +{\*\bkmkend copyright}\hich\af0\dbch\af11\loch\f0 . Subject to the ter\hich\af0\dbch\af11\loch\f0 +ms and conditions of this License, each Contributor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable copyright license to reproduce, prepare Derivative Works of, publicly display, publicly perform, sublicens +\hich\af0\dbch\af11\loch\f0 e\hich\af0\dbch\af11\loch\f0 , and distribute the Work and such Derivative Works in Source or Object form. +\par }{\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7436632\charrsid12260020 {\*\bkmkstart patent}\hich\af0\dbch\af11\loch\f0 3. Grant of Patent License}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid7436632\charrsid12260020 +{\*\bkmkend patent}\hich\af0\dbch\af11\loch\f0 . Subject to the terms and conditions of this License, each Contrib\hich\af0\dbch\af11\loch\f0 +utor hereby grants to You a perpetual, worldwide, non-exclusive, no-charge, royalty-free, irrevocable (except as stated in this section) patent license to make, have made, use, offer to sell, sell, import, and otherwise transfer the Work, where such licen +\hich\af0\dbch\af11\loch\f0 s\hich\af0\dbch\af11\loch\f0 +e applies only to those patent claims licensable by such Contributor that are necessarily infringed by their Contribution(s) alone or by combination of their Contribution(s) with the Work to which such Contribution(s) was submitted. If You institute paten +\hich\af0\dbch\af11\loch\f0 t\hich\af0\dbch\af11\loch\f0 + litigation against any entity (including a cross-claim or counterclaim in a lawsuit) alleging that the Work or a Contribution incorporated within the Work constitutes direct or contributory patent infringement, then any patent licenses granted to You und +\hich\af0\dbch\af11\loch\f0 e\hich\af0\dbch\af11\loch\f0 r this License for that Work shall terminate as of the date such litigation is filed. +\par }{\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7436632\charrsid12260020 {\*\bkmkstart redistribution}\hich\af0\dbch\af11\loch\f0 4. Redistribution}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid7436632\charrsid12260020 +{\*\bkmkend redistribution}\hich\af0\dbch\af11\loch\f0 . You may reproduce and distribute copies of the Work or Derivative Works thereof in any medium, with or without modificatio\hich\af0\dbch\af11\loch\f0 +ns, and in Source or Object form, provided that You meet the following conditions: +\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 a.\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\sb100\sa240\sbauto1\widctlpar +\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls21\adjustright\rin0\lin720\itap0\pararsid7436632 {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 +You must give any other recipients of the Work or Derivative Works a copy of this License; and +\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 b.\tab}\hich\af0\dbch\af11\loch\f0 You must cause any modified files to carry prominent notices stating t\hich\af0\dbch\af11\loch\f0 +hat You changed the files; and +\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 c.\tab}\hich\af0\dbch\af11\loch\f0 +You must retain, in the Source form of any Derivative Works that You distribute, all copyright, patent, trademark, and attribution notices from the Source form of the Work, excluding those notices that do not pertain to a\hich\af0\dbch\af11\loch\f0 +ny part of the Derivative Works; and +\par {\listtext\pard\plain\ltrpar \rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 d.\tab}}\pard \ltrpar\ql \fi-360\li720\ri0\sb100\sa100\sbauto1\saauto1\widctlpar +\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls21\adjustright\rin0\lin720\itap0\pararsid7436632 {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 +If the Work includes a "NOTICE" text file as part of its distribution, then any Derivative Works that You distribute must include a readable copy of the attribution notices contained within such NOTICE file, excludi\hich\af0\dbch\af11\loch\f0 +ng those notices that do not pertain to any part of the Derivative Works, in at least one of the following places: within a NOTICE text file distributed as part of the Derivative Works; within the Source form or documentation, if provided along with the D +\hich\af0\dbch\af11\loch\f0 e\hich\af0\dbch\af11\loch\f0 +rivative Works; or, within a display generated by the Derivative Works, if and wherever such third-party notices normally appear. The contents of the NOTICE file are for informational purposes only and do not modify the License. You may add Your own attri +\hich\af0\dbch\af11\loch\f0 b\hich\af0\dbch\af11\loch\f0 +ution notices within Derivative Works that You distribute, alongside or as an addendum to the NOTICE text from the Work, provided that such additional attribution notices cannot be construed as modifying the License. +\par }\pard \ltrpar\ql \li0\ri0\sb120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7436632 {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 +You may add Your own copyright stateme\hich\af0\dbch\af11\loch\f0 +nt to Your modifications and may provide additional or different license terms and conditions for use, reproduction, or distribution of Your modifications, or for any such Derivative Works as a whole, provided Your use, reproduction, and distribution of t +\hich\af0\dbch\af11\loch\f0 h\hich\af0\dbch\af11\loch\f0 e Work otherwise complies with the conditions stated in this License. +\par }\pard \ltrpar\ql \li0\ri0\sb100\sa100\sbauto1\saauto1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7436632 {\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7436632\charrsid12260020 {\*\bkmkstart contributions} +\hich\af0\dbch\af11\loch\f0 5. Submission of Contributions}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid7436632\charrsid12260020 {\*\bkmkend contributions}\hich\af0\dbch\af11\loch\f0 +. Unless You explicitly state otherwise, any Contribution intentionally submitted for inclusion in the Work by You to the Licenso\hich\af0\dbch\af11\loch\f0 +r shall be under the terms and conditions of this License, without any additional terms or conditions. Notwithstanding the above, nothing herein shall supersede or modify the terms of any separate license agreement you may have executed with Licensor rega +\hich\af0\dbch\af11\loch\f0 r\hich\af0\dbch\af11\loch\f0 ding such Contributions. +\par }{\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7436632\charrsid12260020 {\*\bkmkstart trademarks}\hich\af0\dbch\af11\loch\f0 6. Trademarks}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid7436632\charrsid12260020 {\*\bkmkend trademarks} +\hich\af0\dbch\af11\loch\f0 . This License does not grant permission to use the trade names, trademarks, service marks, or product names of the Licensor, except as required for reasonable and customary use in describing the o\hich\af0\dbch\af11\loch\f0 +rigin of the Work and reproducing the content of the NOTICE file. +\par }{\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7436632\charrsid12260020 {\*\bkmkstart no-warranty}{\*\bkmkend no-warranty}\hich\af0\dbch\af11\loch\f0 7. Disclaimer of Warranty}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 +\f0\fs24\insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 . Unless required by applicable law or agreed to in writing, Licensor provides the Work (and each Contributor provides its Contributions) on a\hich\af0\dbch\af11\loch\f0 +n "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied, including, without limitation, any warranties or conditions of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A PARTICULAR PURPOSE. You are solely responsi +\hich\af0\dbch\af11\loch\f0 b\hich\af0\dbch\af11\loch\f0 le for determining the appropriateness of using or redistributing the Work and assume any risks associated with Your exercise of permissions under this License. +\par }{\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7436632\charrsid12260020 {\*\bkmkstart no-liability}{\*\bkmkend no-liability}\hich\af0\dbch\af11\loch\f0 8. Limitation of Liability}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 +\f0\fs24\insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 . In no event and under no legal theory, whe\hich\af0\dbch\af11\loch\f0 +ther in tort (including negligence), contract, or otherwise, unless required by applicable law (such as deliberate and grossly negligent acts) or agreed to in writing, shall any Contributor be liable to You for damages, including any direct, indirect, spe +\hich\af0\dbch\af11\loch\f0 c\hich\af0\dbch\af11\loch\f0 +ial, incidental, or consequential damages of any character arising as a result of this License or out of the use or inability to use the Work (including but not limited to damages for loss of goodwill, work stoppage, computer failure or malfunction, or an +\hich\af0\dbch\af11\loch\f0 y\hich\af0\dbch\af11\loch\f0 and all other commercial damages or losses), even if such Contributor has been advised of the possibility of such damages. +\par }{\rtlch\fcs1 \ab\af38\afs24 \ltrch\fcs0 \b\f0\fs24\insrsid7436632\charrsid12260020 {\*\bkmkstart additional}\hich\af0\dbch\af11\loch\f0 9. Accepting Warranty or Additional Liability}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 +\f0\fs24\insrsid7436632\charrsid12260020 {\*\bkmkend additional}\hich\af0\dbch\af11\loch\f0 . While redistributing the Work or Derivative Works thereof, You m\hich\af0\dbch\af11\loch\f0 +ay choose to offer, and charge a fee for, acceptance of support, warranty, indemnity, or other liability obligations and/or rights consistent with this License. However, in accepting such obligations, You may act only on Your own behalf and on Your sole r +\hich\af0\dbch\af11\loch\f0 e\hich\af0\dbch\af11\loch\f0 +sponsibility, not on behalf of any other Contributor, and only if You agree to indemnify, defend, and hold each Contributor harmless for any liability incurred by, or claims asserted against, such Contributor by reason of your accepting any such warranty +\hich\af0\dbch\af11\loch\f0 o\hich\af0\dbch\af11\loch\f0 r additional liability. +\par \hich\af0\dbch\af11\loch\f0 See }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 + HYPERLINK "http://www.codeplex.com/IronPython/Wiki/View.aspx?title=FAQ&referringTitle=Home" }{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid7436632\charrsid12260020 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90bb800000068007400740070003a002f002f007700770077002e0063006f006400650070006c00650078002e0063006f006d002f00490072006f006e0050007900740068006f006e002f00570069006b0069002f005600 +6900650077002e0061007300700078003f007400690074006c0065003d00460041005100260072006500660065007200720069006e0067005400690074006c0065003d0048006f006d0065000000795881f43b1d7f48af2c825dc485276300000000a5ab00002e}}}{\fldrslt {\rtlch\fcs1 \af38\afs24 +\ltrch\fcs0 \f0\fs24\ul\cf2\insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 FAQ}}}\sectd \ltrsect\linex0\sectdefaultcl\sftnbj {\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid7436632\charrsid12260020 \hich\af0\dbch\af11\loch\f0 + for answers to frequently asked questions about this license.}{\rtlch\fcs1 \af38\afs24 \ltrch\fcs0 \f0\fs24\insrsid7436632\charrsid9785620 +\par }\pard \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid7436632 {\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid9964378\charrsid7436632 +\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a +9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad +5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 +b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 +0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 +a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f +c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 +0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 +a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 +6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b +4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b +4757e8d3f729e245eb2b260a0238fd010000ffff0300504b03041400060008000000210096b5ade296060000501b0000160000007468656d652f7468656d652f +7468656d65312e786d6cec594f6fdb3614bf0fd87720746f6327761a07758ad8b19b2d4d1bc46e871e698996d850a240d2497d1bdae38001c3ba618715d86d87 +615b8116d8a5fb34d93a6c1dd0afb0475292c5585e9236d88aad3e2412f9e3fbff1e1fa9abd7eec70c1d1221294fda5efd72cd4324f1794093b0eddd1ef62fad +79482a9c0498f184b4bd2991deb58df7dfbb8ad755446282607d22d771db8b944ad79796a40fc3585ee62949606ecc458c15bc8a702910f808e8c66c69b9565b +5d8a314d3c94e018c8de1a8fa94fd05093f43672e23d06af89927ac06762a049136785c10607758d9053d965021d62d6f6804fc08f86e4bef210c352c144dbab +999fb7b4717509af678b985ab0b6b4ae6f7ed9ba6c4170b06c788a705430adf71bad2b5b057d03606a1ed7ebf5babd7a41cf00b0ef83a6569632cd467faddec9 +699640f6719e76b7d6ac355c7c89feca9cccad4ea7d36c65b258a206641f1b73f8b5da6a6373d9c11b90c537e7f08dce66b7bbeae00dc8e257e7f0fd2badd586 +8b37a088d1e4600ead1ddaef67d40bc898b3ed4af81ac0d76a197c86826828a24bb318f3442d8ab518dfe3a20f000d6458d104a9694ac6d88728eee2782428d6 +0cf03ac1a5193be4cbb921cd0b495fd054b5bd0f530c1931a3f7eaf9f7af9e3f45c70f9e1d3ff8e9f8e1c3e3073f5a42ceaa6d9c84e5552fbffdeccfc71fa33f +9e7ef3f2d117d57859c6fffac327bffcfc793510d26726ce8b2f9ffcf6ecc98baf3efdfdbb4715f04d814765f890c644a29be408edf3181433567125272371be +15c308d3f28acd249438c19a4b05fd9e8a1cf4cd296699771c393ac4b5e01d01e5a30a787d72cf1178108989a2159c77a2d801ee72ce3a5c545a6147f32a9979 +3849c26ae66252c6ed637c58c5bb8b13c7bfbd490a75330f4b47f16e441c31f7184e140e494214d273fc80900aedee52ead87597fa824b3e56e82e451d4c2b4d +32a423279a668bb6690c7e9956e90cfe766cb37b077538abd27a8b1cba48c80acc2a841f12e698f13a9e281c57911ce298950d7e03aba84ac8c154f8655c4f2a +f074481847bd804859b5e696007d4b4edfc150b12addbecba6b18b148a1e54d1bc81392f23b7f84137c2715a851dd0242a633f900710a218ed715505dfe56e86 +e877f0034e16bafb0e258ebb4faf06b769e888340b103d3311da9750aa9d0a1cd3e4efca31a3508f6d0c5c5c398602f8e2ebc71591f5b616e24dd893aa3261fb +44f95d843b5974bb5c04f4edafb95b7892ec1108f3f98de75dc97d5772bdff7cc95d94cf672db4b3da0a6557f70db629362d72bcb0431e53c6066acac80d699a +6409fb44d08741bdce9c0e4971624a2378cceaba830b05366b90e0ea23aaa241845368b0eb9e2612ca8c742851ca251ceccc70256d8d87265dd96361531f186c +3d9058edf2c00eafe8e1fc5c509031bb4d680e9f39a3154de0accc56ae644441edd76156d7429d995bdd88664a9dc3ad50197c38af1a0c16d684060441db0256 +5e85f3b9660d0713cc48a0ed6ef7dedc2dc60b17e92219e180643ed27acffba86e9c94c78ab90980d8a9f0913ee49d62b512b79626fb06dccee2a432bbc60276 +b9f7dec44b7904cfbca4f3f6443ab2a49c9c2c41476dafd55c6e7ac8c769db1bc399161ee314bc2e75cf8759081743be1236ec4f4d6693e5336fb672c5dc24a8 +c33585b5fb9cc24e1d4885545b58463634cc5416022cd19cacfccb4d30eb45296023fd35a458598360f8d7a4003bbaae25e331f155d9d9a5116d3bfb9a95523e +51440ca2e0088dd844ec6370bf0e55d027a012ae264c45d02f708fa6ad6da6dce29c255df9f6cae0ec38666984b372ab5334cf640b37795cc860de4ae2816e95 +b21be5ceaf8a49f90b52a51cc6ff3355f47e0237052b81f6800fd7b802239daf6d8f0b1571a8426944fdbe80c6c1d40e8816b88b8569082ab84c36ff0539d4ff +6dce591a26ade1c0a7f669880485fd484582903d284b26fa4e2156cff62e4b9265844c4495c495a9157b440e091bea1ab8aaf7760f4510eaa69a6465c0e04ec6 +9ffb9e65d028d44d4e39df9c1a52ecbd3607fee9cec7263328e5d661d3d0e4f62f44acd855ed7ab33cdf7bcb8ae889599bd5c8b3029895b6825696f6af29c239 +b75a5bb1e6345e6ee6c28117e73586c1a2214ae1be07e93fb0ff51e133fb65426fa843be0fb515c187064d0cc206a2fa926d3c902e907670048d931db4c1a449 +59d366ad93b65abe595f70a75bf03d616c2dd959fc7d4e6317cd99cbcec9c58b34766661c7d6766ca1a9c1b327531486c6f941c638c67cd22a7f75e2a37be0e8 +2db8df9f30254d30c1372581a1f51c983c80e4b71ccdd28dbf000000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468 +656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4 +350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d2624 +52282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe5141 +73d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c020000130000000000000000 +0000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b00000000000000 +000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c0000000000000000000000000019 +0200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d001400060008000000210096b5ade296060000501b00001600000000 +000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b01000027 +00000000000000000000000000a00900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d0100009b0a00000000} +{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d +617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 +6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 +656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} +{\*\latentstyles\lsdstimax267\lsdlockeddef0\lsdsemihiddendef1\lsdunhideuseddef1\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9; +\lsdpriority39 \lsdlocked0 toc 1;\lsdpriority39 \lsdlocked0 toc 2;\lsdpriority39 \lsdlocked0 toc 3;\lsdpriority39 \lsdlocked0 toc 4;\lsdpriority39 \lsdlocked0 toc 5;\lsdpriority39 \lsdlocked0 toc 6;\lsdpriority39 \lsdlocked0 toc 7; +\lsdpriority39 \lsdlocked0 toc 8;\lsdpriority39 \lsdlocked0 toc 9;\lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdpriority1 \lsdlocked0 Default Paragraph Font; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority59 \lsdlocked0 Table Grid;\lsdunhideused0 \lsdlocked0 Placeholder Text;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdunhideused0 \lsdlocked0 Revision; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdpriority37 \lsdlocked0 Bibliography;\lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;}}{\*\datastore 010500000200000018000000 +4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 +d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e500000000000000000000000080b2 +d2bb7c45cb01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools.csproj b/Tools/IronStudio/IronRubyTools/IronRubyTools.csproj new file mode 100644 index 0000000000..62971cb7b6 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools.csproj @@ -0,0 +1,390 @@ + + + + + Debug + AnyCPU + 2.0 + {82b43b9b-a64c-4715-b499-d71e9ca2bd60};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + Library + Properties + Microsoft + IronRubyTools + v4.0 + {3467BF9A-3A0F-42BE-85E3-E818E7D3E2E8} + SAK + SAK + SAK + SAK + + + true + full + false + $(SolutionDir)..\bin\Debug + DEBUG;TRACE;$(SignedSym) + prompt + 4 + true + + + pdbonly + true + $(SolutionDir)..\bin\Release + TRACE;$(SignedSym) + prompt + 4 + true + + + + {B6C04406-5B05-4E66-AB36-8FD9A8D14165} + IronRubyToolsCore + + BuiltProjectOutputGroup + + + DebugSymbolsProjectOutputGroup + + False + + + {843716AE-38B3-4723-963C-950DD06BC4B8} + IronStudioCore + + + + + False + + + {C98DE16D-417E-4079-8939-1F8C8E28E2C8} + IronStudio + + + + + False + + + {AA18A245-E342-4368-A474-83178311A742} + IronRuby.Libraries.Yaml + + BuiltProjectOutputGroup + + + DebugSymbolsProjectOutputGroup + + + + {77323B06-15A2-4CF4-8A7A-86EAA2B66498} + IronRuby.Libraries + + BuiltProjectOutputGroup + + + DebugSymbolsProjectOutputGroup + + + + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD} + Ruby + + BuiltProjectOutputGroup + + + DebugSymbolsProjectOutputGroup + + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Dynamic + + + + + False + + + {02FF0909-F5AD-48CF-A86A-345E721B7E40} + Microsoft.Scripting + + + + + False + + + + + False + + + False + + + False + + + False + + + + + + + + + + + + False + + + + + + False + $(DevEnvDir)\PublicAssemblies\Microsoft.VisualStudio.Language.NavigateTo.Interfaces.dll + + + + + global + + + + + + + + + + + + + + + + False + + + + + + + + + + + + + + + True + + + True + + + True + + + True + + + + + + + + + + + + + + + + + + + UserControl + + + RubyOptionsControl.cs + + + Component + + + + + + + + + + + + + + + + + + + + + + True + True + Resources.resx + + + + + + + + + + 1000 + Designer + + + Designer + + + Always + true + + + true + + + true + + + true + + + + + + + true + + + + + + + true + + + + + + + true + + + + + + + + true + + + + + + + + true + + + + + + + + + true + + + + + + + + + + true + + + + + + + + + + true + + + + + + + + + + + + + + + + + + + + + RubyOptionsControl.cs + + + + + Designer + Microsoft.IronRubyTools + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + true + Microsoft.IronRubyTools + + + + + true + true + + + + + diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools.csproj.user b/Tools/IronStudio/IronRubyTools/IronRubyTools.csproj.user new file mode 100644 index 0000000000..fe9461292c --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools.csproj.user @@ -0,0 +1,8 @@ + + + + Program + C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\devenv.exe + /rootsuffix Exp /log C:\temp\devenv.xml + + \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools.csproj.vspscc b/Tools/IronStudio/IronRubyTools/IronRubyTools.csproj.vspscc new file mode 100644 index 0000000000..feffdecaa4 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools.vsct b/Tools/IronStudio/IronRubyTools/IronRubyTools.vsct new file mode 100644 index 0000000000..725bc9674e --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools.vsct @@ -0,0 +1,159 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + IronRuby Tools + IronR&uby Tools + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Commands/Command.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Commands/Command.cs new file mode 100644 index 0000000000..a97a0811b8 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Commands/Command.cs @@ -0,0 +1,62 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio.OLE.Interop; + +namespace Microsoft.IronRubyTools.Commands { + /// + /// Base class for available commands. To add a new command you must first update the .vsct file so that + /// our commands are registered and available. Then you need to subclass Command and at a new instance of + /// the command in CommandTable. IronRubyToolsPackage will then register the command on startup. + /// + abstract class Command { + /// + /// Provides the implementation of what should happen when the command is executed. + /// + /// sender is the MenuCommand or OleMenuCommand object which is causing the event to be fired. + /// + public abstract void DoCommand(object sender, EventArgs args); + + /// + /// Enables a command to hook into our edit filter for Ruby text buffers. + /// + /// Called with the OLECMD object for the command being processed. Returns null + /// if the command does not want to handle this message or the HRESULT that + /// should be returned from the QueryStatus call. + /// + public virtual int? EditFilterQueryStatus(ref OLECMD cmd, IntPtr pCmdText) { + return null; + } + + /// + /// Provides the CommandId for this command which corresponds to the CommandId in the vsct file + /// and PkgCmdId.cs. + /// + public abstract int CommandId { + get; + } + + /// + /// Provides an event handler that will be invoked before the menu containing the command + /// is displayed. This can enable, disable, or hide the menu command. By default returns + /// null. + /// + public virtual EventHandler BeforeQueryStatus { + get { + return null; + } + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Commands/CommandTable.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Commands/CommandTable.cs new file mode 100644 index 0000000000..77ee612520 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Commands/CommandTable.cs @@ -0,0 +1,26 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +namespace Microsoft.IronRubyTools.Commands { + /// + /// List of all commands defined for IronRuby package + /// + static class CommandTable { + public static Command[] Commands = new Command[] { + new ExecuteInReplCommand(), + new OpenReplCommand(), + new SendToReplCommand() + }; + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Commands/ExecuteInReplCommand.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Commands/ExecuteInReplCommand.cs new file mode 100644 index 0000000000..4abd80e356 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Commands/ExecuteInReplCommand.cs @@ -0,0 +1,145 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Linq; +using System.Threading; +using Microsoft.IronRubyTools.Library.Repl; +using Microsoft.IronRubyTools.Navigation; +using Microsoft.IronRubyTools.Project; +using Microsoft.IronRubyTools.Repl; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Repl; +using Microsoft.Scripting.Utils; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.IronRubyTools.Commands { + /// + /// Provides the command for starting a file or the start item of a project in the REPL window. + /// + internal sealed class ExecuteInReplCommand : Command { + public static ExecuteInReplCommand Instance; + private static Guid _replGuid = new Guid("d9b67029-3a2b-49d1-814b-db21f733c16a"); + + public ExecuteInReplCommand() { + Instance = this; + } + + internal static ToolWindowPane/*!*/ EnsureReplWindow() { + var compModel = IronRubyToolsPackage.ComponentModel; + var provider = compModel.GetExtensions().First(); + + var window = (VsReplWindow)provider.FindReplWindow(_replGuid); + if (window == null) { + var evaluator = new RemoteRubyVsEvaluator(); + evaluator.Initialize(); + window = (VsReplWindow)provider.CreateReplWindow( + evaluator, + IronRubyToolsPackage.Instance.ContentType, + "IronRuby Interactive", + typeof(RubyLanguageInfo).GUID, + _replGuid + ); + window.UseSmartUpDown = IronRubyToolsPackage.Instance.OptionsPage.ReplSmartHistory; + } + return window; + } + + internal static VsReplWindow TryGetReplWindow() { + var compModel = IronRubyToolsPackage.ComponentModel; + var provider = compModel.GetExtensions().First(); + + return (VsReplWindow)provider.FindReplWindow(_replGuid); + } + + public override EventHandler BeforeQueryStatus { + get { + return QueryStatusMethod; + } + } + + private void QueryStatusMethod(object sender, EventArgs args) { + var oleMenu = sender as OleMenuCommand; + + IWpfTextView textView; + var rbProj = CommonPackage.GetStartupProject() as RubyProjectNode; + if (rbProj != null) { + // startup project, enabled in Start in REPL mode. + oleMenu.Visible = true; + oleMenu.Enabled = true; + oleMenu.Supported = true; + oleMenu.Text = "Execute Project in IronRuby Interactive"; + } else if ((textView = CommonPackage.GetActiveTextView()) != null && + textView.TextBuffer.ContentType == IronRubyToolsPackage.Instance.ContentType) { + // enabled in Execute File mode... + oleMenu.Visible = true; + oleMenu.Enabled = true; + oleMenu.Supported = true; + oleMenu.Text = "Execute File in IronRuby Interactive"; + } else { + oleMenu.Visible = false; + oleMenu.Enabled = false; + oleMenu.Supported = false; + } + } + + public override void DoCommand(object sender, EventArgs args) { + var window = (IReplWindow)EnsureReplWindow(); + IVsWindowFrame windowFrame = (IVsWindowFrame)((ToolWindowPane)window).Frame; + + string filename, dir; + if (!CommonPackage.TryGetStartupFileAndDirectory(out filename, out dir)) { + // TODO: Error reporting + return; + } + + ErrorHandler.ThrowOnFailure(windowFrame.Show()); + window.Focus(); + + ((RemoteRubyEvaluator)window.Evaluator).Reset(); + + window.WriteLine(String.Format("Running {0}", filename)); + string scopeName = Path.GetFileNameWithoutExtension(filename); + // now execute the current file in the REPL + var engine = ((RemoteRubyEvaluator)window.Evaluator).Engine; + ThreadPool.QueueUserWorkItem( + _ => { + try { + var src = engine.CreateScriptSourceFromFile(filename, StringUtils.DefaultEncoding, Scripting.SourceCodeKind.Statements); + src.Compile().Execute(((RemoteRubyEvaluator)window.Evaluator).CurrentScope); + } catch (Exception e) { + window.WriteLine(String.Format("Exception: {0}", e)); + } + } + ); + } + + private RubyProjectNode GetRubyStartupProject() { + var buildMgr = (IVsSolutionBuildManager)Package.GetGlobalService(typeof(IVsSolutionBuildManager)); + IVsHierarchy hierarchy; + if (ErrorHandler.Succeeded(buildMgr.get_StartupProject(out hierarchy)) && hierarchy != null) { + return hierarchy.GetProject().GetRubyProject(); + } + return null; + } + + public override int CommandId { + get { return (int)PkgCmdIDList.cmdidExecuteFileInRepl; } + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Commands/OpenReplCommand.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Commands/OpenReplCommand.cs new file mode 100644 index 0000000000..e857076a56 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Commands/OpenReplCommand.cs @@ -0,0 +1,38 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.IronRubyTools.Library.Repl; +using Microsoft.IronStudio.Repl; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.IronRubyTools.Commands { + /// + /// Provides the command for starting the IronRuby REPL window. + /// + class OpenReplCommand : Command { + public override void DoCommand(object sender, EventArgs args) { + var window = ExecuteInReplCommand.EnsureReplWindow(); + + IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame; + ErrorHandler.ThrowOnFailure(windowFrame.Show()); + ((IReplWindow)window).Focus(); + } + + public override int CommandId { + get { return (int)PkgCmdIDList.cmdidReplWindow; } + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Commands/SendToReplCommand.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Commands/SendToReplCommand.cs new file mode 100644 index 0000000000..766550a6c2 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Commands/SendToReplCommand.cs @@ -0,0 +1,72 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.IronStudio.Repl; +using Microsoft.IronStudio; + +namespace Microsoft.IronRubyTools.Commands { + /// + /// Provides the command to send selected text from a buffer to the remote REPL window. + /// + class SendToReplCommand : Command { + public override void DoCommand(object sender, EventArgs args) { + var window = ExecuteInReplCommand.EnsureReplWindow(); + + IVsWindowFrame windowFrame = (IVsWindowFrame)window.Frame; + ErrorHandler.ThrowOnFailure(windowFrame.Show()); + + var activeView = CommonPackage.GetActiveTextView(); + + foreach (var span in activeView.Selection.SelectedSpans) { + var text = span.GetText(); + ((IReplWindow)window).PasteText(text); + } + + ((IReplWindow)window).Focus(); + } + + public override int? EditFilterQueryStatus(ref VisualStudio.OLE.Interop.OLECMD cmd, IntPtr pCmdText) { + var activeView = CommonPackage.GetActiveTextView(); + if (activeView != null && activeView.TextBuffer.ContentType.IsOfType(RubyCoreConstants.ContentType)) { + if (activeView.Selection.IsEmpty || activeView.Selection.Mode == TextSelectionMode.Box) { + cmd.cmdf = (uint)(OLECMDF.OLECMDF_SUPPORTED); + } else { + cmd.cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); + } + } else { + cmd.cmdf = (uint)(OLECMDF.OLECMDF_INVISIBLE); + } + + return VSConstants.S_OK; + } + + public override EventHandler BeforeQueryStatus { + get { + return (sender, args) => { + ((OleMenuCommand)sender).Visible = false; + ((OleMenuCommand)sender).Supported = false; + }; + } + } + public override int CommandId { + get { return (int)PkgCmdIDList.cmdidSendToRepl; } + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Editor/TextViewCreationListener.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Editor/TextViewCreationListener.cs new file mode 100644 index 0000000000..90804f242e --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Editor/TextViewCreationListener.cs @@ -0,0 +1,42 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel.Composition; +using Microsoft.IronRubyTools.Editor.Core; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronRubyTools.Editor { + /// + /// Watches for text views to be created for IronRuby code. Then wires up whatever event handling + /// we're interested in. Currently this just handles highlighting matching braces but we should replace + /// our CodeWindowManager created via our language service and instead just use this. + /// + [Export(typeof(IVsTextViewCreationListener))] + [TextViewRole(PredefinedTextViewRoles.Editable)] + [ContentType(RubyCoreConstants.ContentType)] + class TextViewCreationListener : IVsTextViewCreationListener { + [Import] + internal IVsEditorAdaptersFactoryService AdapterService = null; + + public void VsTextViewCreated(VisualStudio.TextManager.Interop.IVsTextView textViewAdapter) { + ITextView textView = AdapterService.GetWpfTextView(textViewAdapter); + if (textView != null) { + BraceMatcher.WatchBraceHighlights(textView, IronRubyToolsPackage.ComponentModel); + } + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Intellisense/IntellisenseController.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Intellisense/IntellisenseController.cs new file mode 100644 index 0000000000..4cc849ec13 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Intellisense/IntellisenseController.cs @@ -0,0 +1,394 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Windows.Input; +using Microsoft.IronStudio.Repl; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Operations; +using VSConstants = Microsoft.VisualStudio.VSConstants; + +namespace Microsoft.IronRubyTools.Intellisense { + + class IntellisenseController : IIntellisenseController, IOleCommandTarget { + private readonly ITextView _textView; + private readonly IList _subjectBuffers; + private readonly IntellisenseControllerProvider _provider; +#if FEATURE_INTELLISENSE + private ICompletionSession _activeSession; + private ISignatureHelpSession _sigHelpSession; + private IQuickInfoSession _quickInfoSession; +#endif + private IOleCommandTarget _oldTarget; + private IEditorOperations _editOps; + private IntellisensePreKeyProcessor _preProcessor; + + /// + /// Attaches events for invoking Statement completion + /// + /// + /// + /// + public IntellisenseController(IntellisenseControllerProvider provider, IList subjectBuffers, ITextView textView) { + _subjectBuffers = subjectBuffers; + _textView = textView; + _provider = provider; + _editOps = provider._EditOperationsFactory.GetEditorOperations(textView); +#if FEATURE_INTELLISENSE + _textView.MouseHover += new EventHandler(TextViewMouseHover); +#endif + textView.Properties.AddProperty(typeof(IntellisenseController), this); // added so our key processors can get back to us + } + +#if FEATURE_INTELLISENSE + private void TextViewMouseHover(object sender, MouseHoverEventArgs e) { + if (_quickInfoSession != null && !_quickInfoSession.IsDismissed) { + _quickInfoSession.Dismiss(); + } + + _quickInfoSession = _provider._QuickInfoBroker.TriggerQuickInfo(_textView, _textView.TextSnapshot.CreateTrackingPoint(e.Position, PointTrackingMode.Negative), true); + } +#endif + + public void ConnectSubjectBuffer(ITextBuffer subjectBuffer) { + } + + public void DisconnectSubjectBuffer(ITextBuffer subjectBuffer) { + } + + /// + /// Detaches the events + /// + /// + public void Detach(ITextView textView) { + if (_textView == null) { + throw new InvalidOperationException("Already detached from text view"); + } + if (textView != _textView) { + throw new ArgumentException("Not attached to specified text view", "textView"); + } + + DetachKeyboardFilter(); + +#if FEATURE_INTELLISENSE + if (_preProcessor != null) { + _preProcessor.PreprocessTextInput -= OnPreprocessKeyDown; + } +#endif + } + +#if FEATURE_INTELLISENSE + + /// + /// Triggers Statement completion when appropriate keys are pressed + /// The key combination is CTRL-J or "." + /// The intellisense window is dismissed when one presses ESC key + /// + /// + /// + private void OnPreprocessKeyDown(object sender, TextCompositionEventArgs e) { + // We should only receive pre-process events from our text view + Debug.Assert(sender == _textView); + + // TODO: We should handle = for signature completion of keyword arguments + + // We trigger completions when the user types . or space. Our EditFilter will + // also trigger completions when we receive the VSConstants.VSStd2KCmdID.SHOWMEMBERLIST + // command or the VSConstants.VSStd2KCmdID.COMPLETEWORD command. + // + // We trigger signature help when we receive a "(". We update our current sig when + // we receive a "," and we close sig help when we receive a ")". + + switch (e.Text) { + case ".": + case " ": + //DeleteSelectedSpans(); + TriggerCompletionSession(false); + break; + case "(": OpenParenStartSignatureSession(); break; + case ")": + if (_sigHelpSession != null) { + _sigHelpSession.Dismiss(); + _sigHelpSession = null; + } + break; + case ",": + if (_sigHelpSession == null) { + CommaStartSignatureSession(); + } else { + CommaAdvanceParameter(); + } + break; + } + } + + private void Backspace() { + if (_sigHelpSession != null) { + SnapshotPoint? caretPoint = GetCaretPoint(); + if (caretPoint != null && caretPoint.Value.Position != 0) { + string deleting = _textView.TextSnapshot.GetText(caretPoint.Value.Position - 1, 1); + if (deleting == ",") { + PythonSignature sig = _sigHelpSession.SelectedSignature as PythonSignature; + if (sig != null) { + int curParam = sig.Parameters.IndexOf(sig.CurrentParameter); + + if (curParam > 0) { + sig.SetCurrentParameter(sig.Parameters[curParam - 1]); + } + } + } else if (deleting == "(") { + // TODO: See if we should pop to an outer nesting of signature help + _sigHelpSession.Dismiss(); + } + } + } + } + + private void OpenParenStartSignatureSession() { + if (_activeSession != null) { + // TODO: Should we complete here instead? + _activeSession.Dismiss(); + } + + SnapshotPoint? caretPoint = GetCaretPoint(); + + if (_sigHelpSession != null) { + _sigHelpSession.Dismiss(); + } + + TriggerSignatureHelp(); + } + + private void CommaStartSignatureSession() { + TriggerSignatureHelp(); + } + + private void CommaAdvanceParameter() { + // we advance to the next parameter + // TODO: Take into account params arrays + // TODO: need to parse and see if we have keyword arguments entered into the current signature yet + PythonSignature sig = _sigHelpSession.SelectedSignature as PythonSignature; + if (sig != null) { + int curParam = sig.Parameters.IndexOf(sig.CurrentParameter); + if (curParam != -1) { + if (curParam < sig.Parameters.Count - 1) { + sig.SetCurrentParameter(sig.Parameters[curParam + 1]); + } else { + CommaFindBestSignature(curParam); + } + } + } + } + + private void CommaFindBestSignature(int curParam) { + // see if we have a signature which accomodates this... + + // TODO: We should also take into account param arrays + // TODO: We should also get the types of the arguments and use that to + // pick the best signature when the signature includes types. + foreach (var availableSig in _sigHelpSession.Signatures) { + if (availableSig.Parameters.Count > (curParam + 1)) { + _sigHelpSession.SelectedSignature = availableSig; + + PythonSignature sig = availableSig as PythonSignature; + if (sig != null) { + sig.SetCurrentParameter(sig.Parameters[curParam + 1]); + } + break; + } + } + } + + internal void TriggerCompletionSession(bool completeWord) { + Dismiss(); + + _activeSession = CompletionBroker.TriggerCompletion(_textView); + + if (_activeSession != null) { + if (completeWord && + _activeSession.CompletionSets.Count == 1 && + _activeSession.CompletionSets[0].Completions.Count == 1) { + _activeSession.Commit(); + } else { + AttachKeyboardFilter(); + _activeSession.Dismissed += new EventHandler(OnSessionDismissedOrCommitted); + _activeSession.Committed += new EventHandler(OnSessionDismissedOrCommitted); + } + } + } + + private void TriggerSignatureHelp() { + _sigHelpSession = SignatureBroker.TriggerSignatureHelp(_textView); + + if (_sigHelpSession != null) { + AttachKeyboardFilter(); + _sigHelpSession.Dismissed += new EventHandler(OnSessionDismissedOrCommitted); + } + } + + private void OnSessionDismissedOrCommitted(object sender, System.EventArgs e) { + // We've just been told that our active session was dismissed. We should remove all references to it. + _activeSession = null; + _sigHelpSession = null; + } + + private SnapshotPoint? GetCaretPoint() { + return _textView.Caret.Position.Point.GetPoint( + textBuffer => _subjectBuffers.Contains(textBuffer), + PositionAffinity.Predecessor + ); + } + + private void DeleteSelectedSpans() { + if (!_textView.Selection.IsEmpty) { + _editOps.Delete(); + } + } + + private void Dismiss() { + if (_activeSession != null) { + _activeSession.Dismiss(); + } + } +#endif + internal ICompletionBroker CompletionBroker { + get { + return _provider._CompletionBroker; + } + } + + internal IVsEditorAdaptersFactoryService AdaptersFactory { + get { + return _provider._adaptersFactory; + } + } + + internal ISignatureHelpBroker SignatureBroker { + get { + return _provider._SigBroker; + } + } + + #region Internal Implementation + + /// + /// Attaches two events + /// + internal void Attach(IntellisensePreKeyProcessor preProcessor) { +#if FEATURE_INTELLISENSE + preProcessor.PreprocessTextInput += OnPreprocessKeyDown; +#endif + _preProcessor = preProcessor; + } + + #endregion + + #region IOleCommandTarget Members + + // we need this because VS won't give us certain keyboard events as they're handled before our key processor. These + // include enter and tab both of which we want to complete. + + private void AttachKeyboardFilter() { + if (_oldTarget == null) { + ErrorHandler.ThrowOnFailure(AdaptersFactory.GetViewAdapter(_textView).AddCommandFilter(this, out _oldTarget)); + } + } + + private void DetachKeyboardFilter() { + ErrorHandler.ThrowOnFailure(AdaptersFactory.GetViewAdapter(_textView).RemoveCommandFilter(this)); + } + + public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { +#if FEATURE_INTELLISENSE + + if (_activeSession != null) { + if (pguidCmdGroup == VSConstants.VSStd2K) { + switch ((VSConstants.VSStd2KCmdID)nCmdID) { + case VSConstants.VSStd2KCmdID.RETURN: + if (IronPythonToolsPackage.Instance.OptionsPage.EnterCommitsIntellisense) { + _activeSession.Commit(); + return VSConstants.S_OK; + } else { + _activeSession.Dismiss(); + } + /* + // If the user has typed all of the characters as the completion and presses + // enter we should dismiss & let the text editor receive the enter. For example + // when typing "import sys[ENTER]" completion starts after the space. After typing + // sys the user wants a new line and doesn't want to type enter twice. + if (EnterOnCompleteText()) { + int res = _oldTarget.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); + return res; + }*/ + break; + case VSConstants.VSStd2KCmdID.TAB: + _activeSession.Commit(); + return VSConstants.S_OK; + } + } + } else if (_sigHelpSession != null) { + if (pguidCmdGroup == VSConstants.VSStd2K) { + switch ((VSConstants.VSStd2KCmdID)nCmdID) { + case VSConstants.VSStd2KCmdID.BACKSPACE: + Backspace(); + break; + case VSConstants.VSStd2KCmdID.HOME: + case VSConstants.VSStd2KCmdID.BOL: + case VSConstants.VSStd2KCmdID.BOL_EXT: + case VSConstants.VSStd2KCmdID.END: + case VSConstants.VSStd2KCmdID.WORDPREV: + case VSConstants.VSStd2KCmdID.WORDPREV_EXT: + case VSConstants.VSStd2KCmdID.DELETEWORDLEFT: + _sigHelpSession.Dismiss(); + _sigHelpSession = null; + break; + } + } + } +#endif + return _oldTarget.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); + } + /* + private bool EnterOnCompleteText() { + SnapshotPoint? point = _activeSession.GetTriggerPoint(_textView.TextBuffer.CurrentSnapshot); + if (point.HasValue) { + int chars = _textView.Caret.Position.BufferPosition.Position - point.Value.Position; + var selectionStatus = _activeSession.SelectedCompletionSet.SelectionStatus; + if (chars == selectionStatus.Completion.InsertionText.Length) { + string text = _textView.TextSnapshot.GetText(point.Value.Position, chars); + // TODO: If we match except for case we should commit & send enter. + if (text == selectionStatus.Completion.InsertionText) { + return true; + } + } + } + + return false; + }*/ + + public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { + return _oldTarget.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); + } + + #endregion + } +} + diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Intellisense/IntellisenseControllerProvider.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Intellisense/IntellisenseControllerProvider.cs new file mode 100644 index 0000000000..8e5e487a03 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Intellisense/IntellisenseControllerProvider.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.ComponentModel.Composition; +using Microsoft.IronStudio; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Operations; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronRubyTools.Intellisense { + [Export(typeof(IIntellisenseControllerProvider)), ContentType(RubyCoreConstants.ContentType), Order] + class IntellisenseControllerProvider : IIntellisenseControllerProvider { + [Import] + internal ICompletionBroker _CompletionBroker = null; // Set via MEF + [Import] + internal IEditorOperationsFactoryService _EditOperationsFactory = null; // Set via MEF + [Import] + internal IVsEditorAdaptersFactoryService _adaptersFactory { get; set; } + [Import] + internal ISignatureHelpBroker _SigBroker = null; // Set via MEF + [Import] + internal IQuickInfoBroker _QuickInfoBroker = null; // Set via MEF + + public IIntellisenseController TryCreateIntellisenseController(ITextView textView, IList subjectBuffers) { + // Only use the analyzer if the view is actually file backed. If it is the REPL window + // we don't use this. + if (textView.GetFilePath() != null) { + IronRubyToolsPackage.Instance.Analyzer.AnalyzeTextView(textView); + } + + return new IntellisenseController(this, subjectBuffers, textView); + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Intellisense/IntellisensePreKeyProcessor.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Intellisense/IntellisensePreKeyProcessor.cs new file mode 100644 index 0000000000..8eca895819 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Intellisense/IntellisensePreKeyProcessor.cs @@ -0,0 +1,94 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Windows.Input; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.IronRubyTools.Intellisense { + internal class IntellisensePreKeyProcessor : KeyProcessor { + private ITextView _wpfTextView; + + private event KeyEventHandler _preProcessKeyDownEvent; + private event KeyEventHandler _preProcessKeyUpEvent; + + public IntellisensePreKeyProcessor(IWpfTextView wpfTextView) { + _wpfTextView = wpfTextView; + _wpfTextView.Properties.GetProperty(typeof(IntellisenseController)).Attach(this); + } + + public override bool IsInterestedInHandledEvents { + get { + return true; + } + } + + public override void KeyDown(KeyEventArgs args) { + this.FireKeyDown(args); + base.KeyDown(args); + } + + public override void KeyUp(KeyEventArgs args) { + this.FireKeyUp(args); + base.KeyUp(args); + } + + public override void TextInput(TextCompositionEventArgs args) { + this.FireTextInput(args); + base.TextInput(args); + } + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + #region Public Surface + + internal event KeyEventHandler PreprocessKeyDown { + add { _preProcessKeyDownEvent += value; } + remove { _preProcessKeyDownEvent -= value; } + } + + internal event KeyEventHandler PreprocessKeyUp { + add { _preProcessKeyUpEvent += value; } + remove { _preProcessKeyUpEvent -= value; } + } + + internal event TextCompositionEventHandler PreprocessTextInput; + + #endregion + + //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + #region Private Implementation + + private void FireKeyDown(KeyEventArgs args) { + KeyEventHandler handler = _preProcessKeyDownEvent; + if (handler != null) { + handler(_wpfTextView, args); + } + } + + private void FireKeyUp(KeyEventArgs args) { + KeyEventHandler handler = _preProcessKeyUpEvent; + if (handler != null) { + handler(_wpfTextView, args); + } + } + + private void FireTextInput(TextCompositionEventArgs args) { + TextCompositionEventHandler handler = PreprocessTextInput; + if (handler != null) { + handler(_wpfTextView, args); + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Intellisense/IntellisensePreKeyProcessorProvider.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Intellisense/IntellisensePreKeyProcessorProvider.cs new file mode 100644 index 0000000000..aeed2ccc56 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Intellisense/IntellisensePreKeyProcessorProvider.cs @@ -0,0 +1,31 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel.Composition; +using Microsoft.IronStudio; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronRubyTools.Intellisense { + [Export(typeof(IKeyProcessorProvider))] + [Name("Intellisense Sample Preprocess KeyProcessor")] + [Order(After = "DefaultKeyProcessor")] + [ContentType(RubyCoreConstants.ContentType)] + [TextViewRole(PredefinedTextViewRoles.Editable)] + internal sealed class IntellisensePreKeyProcessorProvider : IKeyProcessorProvider { + public KeyProcessor GetAssociatedProcessor(IWpfTextView wpfTextView) { + return new IntellisensePreKeyProcessor(wpfTextView); + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/CodeWindowManager.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/CodeWindowManager.cs new file mode 100644 index 0000000000..d28a3a28cb --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/CodeWindowManager.cs @@ -0,0 +1,116 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.IronRubyTools.Intellisense; +using Microsoft.IronRubyTools.Language; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Operations; +using Microsoft.VisualStudio.TextManager.Interop; + +namespace Microsoft.IronRubyTools.Navigation { + class CodeWindowManager : IVsCodeWindowManager { + private readonly IVsCodeWindow _window; + private readonly IWpfTextView _textView; + private readonly IEditorOperationsFactoryService _editorOperationsFactory; + private static readonly Dictionary _windows = new Dictionary(); + private DropDownBarClient _client; + + public CodeWindowManager(IVsCodeWindow codeWindow, IWpfTextView textView, IComponentModel componentModel) { + _window = codeWindow; + _textView = textView; + _editorOperationsFactory = componentModel.GetService(); + _textView.Properties.AddProperty(typeof(CodeWindowManager), this); + } + + #region IVsCodeWindowManager Members + + public int AddAdornments() { + IVsTextView textView; + _windows[_textView] = this; + + if (ErrorHandler.Succeeded(_window.GetPrimaryView(out textView))) { + OnNewView(textView); + } + + if (ErrorHandler.Succeeded(_window.GetSecondaryView(out textView))) { + OnNewView(textView); + } + + //if (IronPythonToolsPackage.Instance.LangPrefs.NavigationBar) { + return AddDropDownBar(); + //} + + //return VSConstants.S_OK; + } + + private int AddDropDownBar() { + DropDownBarClient dropDown = _client = new DropDownBarClient( + _textView, + AnalysisItem.GetAnalysis(_textView.TextBuffer) + ); + + IVsDropdownBarManager manager = (IVsDropdownBarManager)_window; + + IVsDropdownBar dropDownBar; + int hr = manager.GetDropdownBar(out dropDownBar); + if (ErrorHandler.Succeeded(hr) && dropDownBar != null) { + hr = manager.RemoveDropdownBar(); + if (!ErrorHandler.Succeeded(hr)) { + return hr; + } + } + hr = manager.AddDropdownBar(2, dropDown); + + return hr; + } + + private int RemoveDropDownBar() { + if (_client != null) { + IVsDropdownBarManager manager = (IVsDropdownBarManager)_window; + _client.Unregister(); + _client = null; + return manager.RemoveDropdownBar(); + } + return VSConstants.S_OK; + } + + public int OnNewView(IVsTextView pView) { + // TODO: We pass _textView which may not be right for split buffers, we need + // to test the case where we split a text file and save it as an existing file? + new EditFilter(_textView, pView); + return VSConstants.S_OK; + } + + public int RemoveAdornments() { + _windows.Remove(_textView); + return RemoveDropDownBar(); + } + + public static void ToggleNavigationBar(bool fEnable) { + foreach (var keyValue in _windows) { + if (fEnable) { + ErrorHandler.ThrowOnFailure(keyValue.Value.AddDropDownBar()); + } else { + ErrorHandler.ThrowOnFailure(keyValue.Value.RemoveDropDownBar()); + } + } + } + + #endregion + } + +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/DropDownBarClient.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/DropDownBarClient.cs new file mode 100644 index 0000000000..23f3c26708 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/DropDownBarClient.cs @@ -0,0 +1,336 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Drawing; +using System.IO; +using System.Windows.Forms; +using System.Windows.Threading; +using IronRuby.Compiler.Ast; +using Microsoft.Scripting; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.TextManager.Interop; +using Microsoft.IronRubyTools.Intellisense; +using Microsoft.Scripting.Utils; +using System.Text; +using System.Linq; +using System.Diagnostics; +using System.Threading; + +namespace Microsoft.IronRubyTools.Navigation { + /// + /// Implements the navigation bar which appears above a source file in the editor. + /// + /// The navigation bar consists of two drop-down boxes. On the left hand side is a list + /// of top level constructs. On the right hand side are list of nested constructs for the + /// currently selected top-level construct. + /// + /// When the user moves the caret the current selections are automatically updated. If the + /// user is inside of a top level construct but not inside any of the available nested + /// constructs then the first element of the nested construct list is selected and displayed + /// grayed out. If the user is inside of no top level constructs then the 1st top-level + /// construct is selected and displayed as grayed out. It's first top-level construct is + /// also displayed as being grayed out. + /// + /// The most difficult part of this is handling the transitions from one state to another. + /// We need to change the current selections due to events from two sources: The first is selections + /// in the drop down and the 2nd is the user navigating within the source code. When a change + /// occurs we may need to update the left hand side (along w/ a corresponding update to the right + /// hand side) or we may need to update the right hand side. If we are transitioning from + /// being outside of a known element to being in a known element we also need to refresh + /// the drop down to remove grayed out elements. + /// + class DropDownBarClient : IVsDropdownBarClient { + private readonly AnalysisItem _classifier; // classifier which is used for providing entries + private readonly Dispatcher _dispatcher; // current dispatcher so we can get back to our thread + private readonly IWpfTextView _textView; // text view we're drop downs for + private Model _entries; + private IVsDropdownBar _dropDownBar; // drop down bar - used to refresh when changes occur + + private static readonly ImageList _imageList = GetImageList(); + + private const int ModuleComboBoxId = 0; + private const int MethodComboBoxId = 1; + + public DropDownBarClient(IWpfTextView textView, AnalysisItem classifier) { + Assert.NotNull(textView, classifier); + + _classifier = classifier; + _classifier.OnNewParseTree += ParserOnNewParseTree; + _textView = textView; + _dispatcher = Dispatcher.CurrentDispatcher; + _textView.Caret.PositionChanged += CaretPositionChanged; + BuildModel(); + } + + internal void Unregister() { + _classifier.OnNewParseTree -= ParserOnNewParseTree; + _textView.Caret.PositionChanged -= CaretPositionChanged; + } + + #region Synchronization + + /// + /// Calculates the members of the drop down for top-level members. + /// + private void BuildModel() { + var tree = _classifier.CurrentTree; + if (tree != null) { + _entries = new ModelBuilder(tree).Build(); + } + } + + /// + /// Wired to parser event for when the parser has completed parsing a new tree and we need + /// to update the navigation bar with the new data. + /// + private void ParserOnNewParseTree(object sender, EventArgs e) { + if (_dropDownBar != null) { + Action callback = () => { + BuildModel(); + int position = _textView.Caret.Position.BufferPosition.Position; + + ModuleEntry newModule = _entries.LocateModule(position); + MethodEntry newMethod = newModule.LocateMethod(position); + _dropDownBar.RefreshCombo(ModuleComboBoxId, newModule.Index); + _dropDownBar.RefreshCombo(MethodComboBoxId, newMethod != null ? newMethod.Index : -1); + }; + + _dispatcher.BeginInvoke(callback, DispatcherPriority.Background); + } + } + + private void CaretPositionChanged(object sender, CaretPositionChangedEventArgs e) { + ActivateEntries(e.NewPosition.BufferPosition.Position); + } + + private void ActivateEntries(int position) { + if (_entries == null) { + return; + } + + int currentModuleIndex; + _dropDownBar.GetCurrentSelection(ModuleComboBoxId, out currentModuleIndex); + + ModuleEntry newModule = _entries.LocateModule(position); + Debug.Assert(newModule != null); + + MethodEntry newMethod = newModule.LocateMethod(position); + int newMethodIndex = newMethod != null ? newMethod.Index : -1; + + if (newModule.Index != currentModuleIndex) { + _dropDownBar.SetCurrentSelection(ModuleComboBoxId, newModule.Index); + _dropDownBar.RefreshCombo(MethodComboBoxId, newMethodIndex); + } else { + int currentMethodIndex; + _dropDownBar.GetCurrentSelection(MethodComboBoxId, out currentMethodIndex); + if (newMethodIndex != currentMethodIndex) { + _dropDownBar.SetCurrentSelection(MethodComboBoxId, newMethodIndex); + } + } + } + + #endregion + + #region IVsDropdownBarClient Members + + /// + /// Gets the attributes for the specified combo box. We return the number of elements that we will + /// display, the various attributes that VS should query for next (text, image, and attributes of + /// the text such as being grayed out), along with the appropriate image list. + /// + /// We always return the # of entries based off our entries list, the exact same image list, and + /// we have VS query for text, image, and text attributes all the time. + /// + public int GetComboAttributes(int iCombo, out uint pcEntries, out uint puEntryType, out IntPtr phImageList) { + uint count = 0; + + if (_entries != null) { + switch (iCombo) { + case ModuleComboBoxId: + count = (uint)_entries.ModuleCount; + break; + + case MethodComboBoxId: + int currentModule; + _dropDownBar.GetCurrentSelection(ModuleComboBoxId, out currentModule); + count = (uint)_entries.GetMethodCount(currentModule); + break; + } + } + + pcEntries = count; + puEntryType = (uint)(DROPDOWNENTRYTYPE.ENTRY_TEXT | DROPDOWNENTRYTYPE.ENTRY_IMAGE | DROPDOWNENTRYTYPE.ENTRY_ATTR); + phImageList = _imageList.Handle; + return VSConstants.S_OK; + } + + public int GetComboTipText(int iCombo, out string pbstrText) { + pbstrText = null; + return VSConstants.S_OK; + } + + /// + /// Gets the entry attributes for the given combo box and index. + /// + /// We always use plain text unless we are not inside of a valid entry + /// for the given combo box. In that case we ensure the 1st item + /// is selected and we gray out the 1st entry. + /// + public int GetEntryAttributes(int iCombo, int iIndex, out uint pAttr) { + pAttr = (uint)DROPDOWNFONTATTR.FONTATTR_PLAIN; + + if (iIndex == 0) { + switch (iCombo) { + case ModuleComboBoxId: + //if (_currentModuleIndex == -1) { + // pAttr = (uint)DROPDOWNFONTATTR.FONTATTR_GRAY; + //} + break; + + case MethodComboBoxId: + //if (_currentMethodIndex == -1) { + // pAttr = (uint)DROPDOWNFONTATTR.FONTATTR_GRAY; + //} + break; + } + } + + return VSConstants.S_OK; + } + + /// + /// Gets the image which is associated with the given index for the + /// given combo box. + /// + public int GetEntryImage(int iCombo, int iIndex, out int piImageIndex) { + piImageIndex = 0; + Debug.Assert(_entries != null); + + switch (iCombo) { + case ModuleComboBoxId: + piImageIndex = _entries.GetModuleImageIndex(iIndex); + break; + + case MethodComboBoxId: + int currentModule; + _dropDownBar.GetCurrentSelection(ModuleComboBoxId, out currentModule); + piImageIndex = _entries.GetMethodImageIndex(currentModule, iIndex); + break; + } + + return VSConstants.S_OK; + } + + /// + /// Gets the text which is displayed for the given index for the + /// given combo box. + /// + public int GetEntryText(int iCombo, int iIndex, out string ppszText) { + ppszText = String.Empty; + switch (iCombo) { + case ModuleComboBoxId: + ppszText = _entries.GetModuleName(iIndex); + break; + + case MethodComboBoxId: + int currentModule; + _dropDownBar.GetCurrentSelection(ModuleComboBoxId, out currentModule); + ppszText = _entries.GetMethodName(currentModule, iIndex); + break; + } + + return VSConstants.S_OK; + } + + public int OnComboGetFocus(int iCombo) { + return VSConstants.S_OK; + } + + public int OnItemChosen(int iCombo, int iIndex) { + int position; + switch (iCombo) { + case ModuleComboBoxId: + position = _entries.GetModuleStart(iIndex); + if (position != -1) { + _dropDownBar.RefreshCombo(MethodComboBoxId, -1); + CenterAndFocus(position); + } + break; + + case MethodComboBoxId: + int currentModule; + _dropDownBar.GetCurrentSelection(ModuleComboBoxId, out currentModule); + position = _entries.GetMethodStart(currentModule, iIndex); + if (position != -1) { + CenterAndFocus(position); + } + break; + } + return VSConstants.S_OK; + } + + public int OnItemSelected(int iCombo, int iIndex) { + return VSConstants.S_OK; + } + + /// + /// Called by VS to provide us with the drop down bar. We can call back + /// on the drop down bar to force VS to refresh the combo box or change + /// the current selection. + /// + public int SetDropdownBar(IVsDropdownBar pDropdownBar) { + _dropDownBar = pDropdownBar; + return VSConstants.S_OK; + } + + #endregion + + #region Implementation Details + + /// + /// Reads our image list from our DLLs resource stream. + /// + private static ImageList GetImageList() { + ImageList list = new ImageList(); + list.ImageSize = new Size(0x10, 0x10); + list.TransparentColor = Color.FromArgb(0xff, 0, 0xff); + Stream manifestResourceStream = typeof(Microsoft.IronStudio.Repl.VsReplWindow).Assembly.GetManifestResourceStream("Microsoft.Resources.completionset.bmp"); + list.Images.AddStrip(new Bitmap(manifestResourceStream)); + return list; + } + + /// + /// Moves the caret to the specified index in the current snapshot. Then updates the view port + /// so that caret will be centered. Finally moves focus to the text view so the user can + /// continue typing. + /// + private void CenterAndFocus(int index) { + _textView.Caret.MoveTo(new SnapshotPoint(_textView.TextBuffer.CurrentSnapshot, index)); + + _textView.ViewScroller.EnsureSpanVisible( + new SnapshotSpan(_textView.TextBuffer.CurrentSnapshot, index, 1), + EnsureSpanVisibleOptions.AlwaysCenter + ); + + ((System.Windows.Controls.Control)_textView).Focus(); + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/EditFilter.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/EditFilter.cs new file mode 100644 index 0000000000..8753ac4945 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/EditFilter.cs @@ -0,0 +1,507 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Windows; +using Microsoft.IronRubyTools.Commands; +using Microsoft.IronRubyTools.Editor.Core; +using Microsoft.IronRubyTools.Navigation; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Core.Repl; +using Microsoft.IronStudio.Navigation; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.TextManager.Interop; +using Microsoft.IronRubyTools.Intellisense; +using IronRuby.Compiler; + +namespace Microsoft.IronRubyTools.Language { + /// + /// IOleCommandTarget implementation for interacting with various editor commands. This enables + /// wiring up most of our features to the VisualStudio editor. We currently support: + /// Goto Definition + /// Find All References + /// Show Member List + /// Complete Word + /// Enable/Disable Outlining + /// Comment/Uncomment block + /// + /// We also support IronRuby specific commands via this class. Currently these commands are + /// added by updating our CommandTable class to contain a new command. These commands also need + /// to be registered in our .vsct file so that VS knows about them. + /// + class EditFilter : IOleCommandTarget { + private readonly IWpfTextView _textView; + private readonly IOleCommandTarget _next; + + public EditFilter(IWpfTextView textView, IVsTextView vsTextView) { + _textView = textView; + ErrorHandler.ThrowOnFailure(vsTextView.AddCommandFilter(this, out _next)); + } + + /// + /// Implements Goto Definition. Called when the user selects Goto Definition from the + /// context menu or hits the hotkey associated with Goto Definition. + /// + /// If there is 1 and only one definition immediately navigates to it. If there are + /// no references displays a dialog box to the user. Otherwise it opens the find + /// symbols dialog with the list of results. + /// + private int GotoDefinition() { +#if FEATURE_INTELLISENSE + UpdateStatusForIncompleteAnalysis(); + + var analysis = GetExpressionAnalysis(); + + StandardGlyphGroup type; + List locations = GetDefinitions(analysis, out type); + + if (locations != null && locations.Count == 1) { + var location = locations[0]; + + location.GotoSource(); + } else if (locations == null || locations.Count == 0) { + MessageBox.Show(String.Format("Cannot go to definition \"{0}\"", analysis.Expression)); + } else { + ShowFindSymbolsDialog(analysis, new SymbolList(analysis.Expression, locations, type)); + } + + return VSConstants.S_OK; +#endif + return VSConstants.S_FALSE; + } + +#if FEATURE_INTELLISENSE + private static List GetDefinitions(ExpressionAnalysis provider, out StandardGlyphGroup type) { + var vars = provider.Variables; + + ObjectType? finalType = null; + + List locations = new List(); + foreach (VariableResult result in vars) { + if (finalType == null) { + finalType = result.Type; + } else if (finalType != result.Type) { + finalType = ObjectType.Multiple; + } + + if (result.Location != null) { + locations.Add(result.Location); + } + } + + if (finalType != null) { + type = finalType.Value.ToGlyphGroup(); + } else { + type = StandardGlyphGroup.GlyphGroupClass; + } + + return locations; + } +#endif + + /// + /// Implements Find All References. Called when the user selects Find All References from + /// the context menu or hits the hotkey associated with find all references. + /// + /// Always opens the Find Symbol Results box to display the results. + /// + private int FindAllReferences() { +#if FEATURE_INTELLISENSE + UpdateStatusForIncompleteAnalysis(); + + var provider = GetExpressionAnalysis(); + + StandardGlyphGroup type; + List definitions = GetDefinitions(provider, out type); + + IEnumerable vars = provider.Variables; + + var references = new List(); + foreach (var v in vars) { + if (v.References != null) { + references.AddRange(v.References); + } + } + + ShowFindSymbolsDialog(provider, new SymbolList(provider.Expression, definitions, references, type)); + + return VSConstants.S_OK; +#endif + return VSConstants.S_FALSE; + } + +#if FEATURE_INTELLISENSE + private static bool IsIdentifierChar(char curChar) { + return Tokenizer.IsIdentifier(curChar, 0x07f); + } + + private void UpdateStatusForIncompleteAnalysis() { + var statusBar = (IVsStatusbar)CommonPackage.GetGlobalService(typeof(SVsStatusbar)); + if (!IronRubyToolsPackage.Instance.Analyzer.IsAnalyzing) { + statusBar.SetText("Ruby source analysis is not up to date"); + } + } + + /// + /// Opens the find symbols dialog with a list of results. This is done by requesting + /// that VS does a search against our library GUID. Our library then responds to + /// that request by extracting the prvoided symbol list out and using that for the + /// search results. + /// + private static void ShowFindSymbolsDialog(ExpressionAnalysis provider, SymbolList symbols) { + // ensure our library is loaded so find all references will go to our library + Package.GetGlobalService(typeof(IRubyLibraryManager)); + + var findSym = (IVsFindSymbol)IronRubyToolsPackage.GetGlobalService(typeof(SVsObjectSearch)); + VSOBSEARCHCRITERIA2 searchCriteria = new VSOBSEARCHCRITERIA2(); + searchCriteria.eSrchType = VSOBSEARCHTYPE.SO_ENTIREWORD; + searchCriteria.pIVsNavInfo = symbols; + searchCriteria.grfOptions = (uint)_VSOBSEARCHOPTIONS2.VSOBSO_LISTREFERENCES; + searchCriteria.szName = provider.Expression; + + Guid guid = Guid.Empty; + ErrorHandler.ThrowOnFailure(findSym.DoSearch(new Guid(CommonConstants.LibraryGuid), new VSOBSEARCHCRITERIA2[] { searchCriteria })); + } + + private ExpressionAnalysis GetExpressionAnalysis() { + var textView = _textView; + var textBuffer = _textView.TextBuffer; + var snapshot = textBuffer.CurrentSnapshot; + int caretPos = _textView.Caret.Position.BufferPosition.Position; + + // foo( + // ^ + // +--- Caret here + // + // We want to lookup foo, not foo( + // + if (caretPos != snapshot.Length) { + string curChar = snapshot.GetText(caretPos, 1); + if (!IsIdentifierChar(curChar[0]) && caretPos > 0) { + string prevChar = snapshot.GetText(caretPos - 1, 1); + if (IsIdentifierChar(prevChar[0])) { + caretPos--; + } + } + } + + var span = snapshot.CreateTrackingSpan( + caretPos, + 0, + SpanTrackingMode.EdgeInclusive + ); + + return Analysis.AnalyzeExpression(snapshot, textBuffer, span); + } +#endif + + class SimpleLocationInfo : SimpleObject, IVsNavInfoNode { + private readonly LocationInfo _locationInfo; + private readonly StandardGlyphGroup _glyphType; + private readonly string _pathText, _lineText; + + public SimpleLocationInfo(string searchText, LocationInfo locInfo, StandardGlyphGroup glyphType) { + _locationInfo = locInfo; + _glyphType = glyphType; + _pathText = GetSearchDisplayText(); + _lineText = _locationInfo.Cookie.GetLine(_locationInfo.Line); + } + + public override string Name { + get { + return _locationInfo.FilePath; + } + } + + public override string GetTextRepresentation(VSTREETEXTOPTIONS options) { + if (options == VSTREETEXTOPTIONS.TTO_DEFAULT) { + return _pathText + _lineText.Trim(); + } + return String.Empty; + } + + private string GetSearchDisplayText() { + return String.Format("{0} - ({1}, {2}): ", + _locationInfo.FilePath, + _locationInfo.Line, + _locationInfo.Column); + } + + public override string UniqueName { + get { + return _locationInfo.FilePath; + } + } + + public override bool CanGoToSource { + get { + return true; + } + } + + public override VSTREEDISPLAYDATA DisplayData { + get { + var res = new VSTREEDISPLAYDATA(); + res.Image = res.SelectedImage = (ushort)_glyphType; + res.State = (uint)_VSTREEDISPLAYSTATE.TDS_FORCESELECT; + + // This code highlights the text but it gets the wrong region. This should be re-enabled + // and highlight the correct region. + + //res.ForceSelectStart = (ushort)(_pathText.Length + _locationInfo.Column - 1); + //res.ForceSelectLength = (ushort)_locationInfo.Length; + return res; + } + } + + public override void GotoSource(VSOBJGOTOSRCTYPE SrcType) { + _locationInfo.GotoSource(); + } + + #region IVsNavInfoNode Members + + public int get_Name(out string pbstrName) { + pbstrName = _locationInfo.FilePath; + return VSConstants.S_OK; + } + + public int get_Type(out uint pllt) { + pllt = 16; // (uint)_LIB_LISTTYPE2.LLT_MEMBERHIERARCHY; + return VSConstants.S_OK; + } + + #endregion + } + + class SymbolList : SimpleObjectList, IVsNavInfo, ICustomSearchListProvider { + public SymbolList(string searchText, List locations, StandardGlyphGroup glyphType) { + foreach (var location in locations) { + Children.Add(new SimpleLocationInfo(searchText, location, glyphType)); + } + } + + public SymbolList(string searchText, List locations, List references, StandardGlyphGroup glyphType) { + foreach (var location in locations) { + Children.Add(new SimpleLocationInfo(searchText, location, glyphType)); + } + foreach (var location in references) { + Children.Add(new SimpleLocationInfo(searchText, location, StandardGlyphGroup.GlyphReference)); + } + } + + class NodeEnumerator : IVsEnumNavInfoNodes { + private readonly List _locations; + private IEnumerator _locationEnum; + + public NodeEnumerator(List locations) { + _locations = locations; + Reset(); + } + + #region IVsEnumNavInfoNodes Members + + public int Clone(out IVsEnumNavInfoNodes ppEnum) { + ppEnum = new NodeEnumerator(_locations); + return VSConstants.S_OK; + } + + public int Next(uint celt, IVsNavInfoNode[] rgelt, out uint pceltFetched) { + pceltFetched = 0; + while (celt-- != 0 && _locationEnum.MoveNext()) { + rgelt[pceltFetched++] = _locationEnum.Current; + } + return VSConstants.S_OK; + } + + public int Reset() { + _locationEnum = _locations.GetEnumerator(); + return VSConstants.S_OK; + } + + public int Skip(uint celt) { + while (celt-- != 0) { + _locationEnum.MoveNext(); + } + return VSConstants.S_OK; + } + + #endregion + } + + #region IVsNavInfo Members + + public int EnumCanonicalNodes(out IVsEnumNavInfoNodes ppEnum) { + ppEnum = new NodeEnumerator(Children); + return VSConstants.S_OK; + } + + public int EnumPresentationNodes(uint dwFlags, out IVsEnumNavInfoNodes ppEnum) { + ppEnum = new NodeEnumerator(Children); + return VSConstants.S_OK; + } + + public int GetLibGuid(out Guid pGuid) { + pGuid = Guid.Empty; + return VSConstants.S_OK; + } + + public int GetSymbolType(out uint pdwType) { + pdwType = (uint)_LIB_LISTTYPE2.LLT_MEMBERHIERARCHY; + return VSConstants.S_OK; + } + + #endregion + + #region ICustomSearchListProvider Members + + public IVsSimpleObjectList2 GetSearchList() { + return this; + } + + #endregion + } + + #region IOleCommandTarget Members + + /// + /// Called from VS when we should handle a command or pass it on. + /// + public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { + // preprocessing + if (pguidCmdGroup == VSConstants.GUID_VSStandardCommandSet97) { + switch ((VSConstants.VSStd97CmdID)nCmdID) { + case VSConstants.VSStd97CmdID.GotoDefn: return GotoDefinition(); + case VSConstants.VSStd97CmdID.FindReferences: return FindAllReferences(); + + } + } else if (pguidCmdGroup == CommonConstants.Std2KCmdGroupGuid) { + OutliningTaggerProvider.OutliningTagger tagger; + switch ((VSConstants.VSStd2KCmdID)nCmdID) { + case VSConstants.VSStd2KCmdID.RETURN: + if (IronRubyToolsPackage.Instance.OptionsPage.AutoIndent) { + AutoIndent.HandleReturn(_textView, (IClassifier)_textView.TextBuffer.Properties.GetProperty(typeof(IDlrClassifier))); + return VSConstants.S_OK; + } + break; +#if FEATURE_INTELLISENSE + case VSConstants.VSStd2KCmdID.SHOWMEMBERLIST: + case VSConstants.VSStd2KCmdID.COMPLETEWORD: + var controller = _textView.Properties.GetProperty(typeof(IntellisenseController)); + if (controller != null) { + controller.TriggerCompletionSession((VSConstants.VSStd2KCmdID)nCmdID == VSConstants.VSStd2KCmdID.COMPLETEWORD); + return VSConstants.S_OK; + } + break; +#endif + + case VSConstants.VSStd2KCmdID.OUTLN_STOP_HIDING_ALL: + tagger = _textView.TextBuffer.GetOutliningTagger(); + if (tagger != null) { + tagger.Disable(); + } + // let VS get the event as well + break; + + case VSConstants.VSStd2KCmdID.OUTLN_START_AUTOHIDING: + tagger = _textView.TextBuffer.GetOutliningTagger(); + if (tagger != null) { + tagger.Enable(); + } + // let VS get the event as well + break; + + case VSConstants.VSStd2KCmdID.COMMENT_BLOCK: + case VSConstants.VSStd2KCmdID.COMMENTBLOCK: + _textView.CommentBlock(); + break; + + case VSConstants.VSStd2KCmdID.UNCOMMENT_BLOCK: + case VSConstants.VSStd2KCmdID.UNCOMMENTBLOCK: + _textView.UncommentBlock(); + break; + } + } + + return _next.Exec(pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); + } + + /// + /// Called from VS to see what commands we support. + /// + public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { + if (pguidCmdGroup == VSConstants.GUID_VSStandardCommandSet97) { + for (int i = 0; i < cCmds; i++) { + switch ((VSConstants.VSStd97CmdID)prgCmds[i].cmdID) { + case VSConstants.VSStd97CmdID.GotoDefn: + prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); + return VSConstants.S_OK; + case VSConstants.VSStd97CmdID.FindReferences: + prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); + return VSConstants.S_OK; + } + } + } else if (pguidCmdGroup == GuidList.guidIronRubyToolsCmdSet) { + for (int i = 0; i < cCmds; i++) { + foreach (var command in CommandTable.Commands) { + if (command.CommandId == prgCmds[i].cmdID) { + int? res = command.EditFilterQueryStatus(ref prgCmds[i], pCmdText); + if (res != null) { + return res.Value; + } + } + } + } + } else if (pguidCmdGroup == CommonConstants.Std2KCmdGroupGuid) { + OutliningTaggerProvider.OutliningTagger tagger; + for (int i = 0; i < cCmds; i++) { + switch ((VSConstants.VSStd2KCmdID)prgCmds[i].cmdID) { + case VSConstants.VSStd2KCmdID.SHOWMEMBERLIST: + case VSConstants.VSStd2KCmdID.COMPLETEWORD: + prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); + return VSConstants.S_OK; + case VSConstants.VSStd2KCmdID.OUTLN_STOP_HIDING_ALL: + tagger = _textView.TextBuffer.GetOutliningTagger(); + if (tagger != null && tagger.Enabled) { + prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); + } + return VSConstants.S_OK; + case VSConstants.VSStd2KCmdID.OUTLN_START_AUTOHIDING: + tagger = _textView.TextBuffer.GetOutliningTagger(); + if (tagger != null && !tagger.Enabled) { + prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); + } + return VSConstants.S_OK; + case VSConstants.VSStd2KCmdID.COMMENT_BLOCK: + case VSConstants.VSStd2KCmdID.COMMENTBLOCK: + case VSConstants.VSStd2KCmdID.UNCOMMENT_BLOCK: + case VSConstants.VSStd2KCmdID.UNCOMMENTBLOCK: + prgCmds[i].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED); + return VSConstants.S_OK; + } + } + } + return _next.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/RubyCodeModel.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/RubyCodeModel.cs new file mode 100644 index 0000000000..767eb8ffed --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/RubyCodeModel.cs @@ -0,0 +1,532 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; +using System.Drawing; +using System.IO; +using System.Windows.Forms; +using System.Windows.Threading; +using IronRuby.Compiler.Ast; +using Microsoft.Scripting; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.TextManager.Interop; +using Microsoft.IronRubyTools.Intellisense; +using Microsoft.Scripting.Utils; +using System.Text; +using System.Linq; +using System.Diagnostics; +using System.Threading; + +namespace Microsoft.IronRubyTools.Navigation { + /// + /// An enum which is synchronized with our image list for the various + /// kinds of images which are available. This can be combined with the + /// ImageListOverlay to select an image for the appropriate member type + /// and indicate the appropiate visiblity. These can be combined with + /// GetImageListIndex to get the final index. + /// + /// Most of these are unused as we're just using an image list shipped + /// by the VS SDK. + /// + internal enum ImageListKind { + Class, + Unknown1, + Unknown2, + Enum, + Unknown3, + Lightning, + Unknown4, + BlueBox, + Key, + BlueStripe, + ThreeDashes, + TwoBoxes, + Method, + StaticMethod, + Unknown6, + Namespace, + Unknown7, + Property, + Unknown8, + Unknown9, + Unknown10, + Unknown11, + Unknown12, + Unknown13, + ClassMethod + } + + /// + /// Indicates the overlay kind which should be used for a drop down members + /// image. The overlay kind typically indicates visibility. + /// + /// Most of these are unused as we're just using an image list shipped + /// by the VS SDK. + /// + internal enum ImageListOverlay { + ImageListOverlayNone, + ImageListOverlayLetter, + ImageListOverlayBlue, + ImageListOverlayKey, + ImageListOverlayPrivate, + ImageListOverlayArrow, + } + + internal sealed class Model { + private readonly ModuleEntry/*!*/ _top; + private readonly ModuleEntry[]/*!*/ _moduleEntries; // entries for modules of the file + + public Model(ModuleEntry/*!*/ top, ModuleEntry[]/*!*/ moduleEntries) { + _top = top; + _moduleEntries = moduleEntries; + } + + public int ModuleCount { + get { return _moduleEntries.Length; } + } + + internal ModuleEntry/*!*/ LocateModule(int position) { + return (ModuleEntry)_top.LocateModule(position); + } + + internal int GetModuleImageIndex(int index) { + return (index >= 0 && index < _moduleEntries.Length) ? _moduleEntries[index].ImageListIndex : 0; + } + + internal string GetModuleName(int index) { + return (index >= 0 && index < _moduleEntries.Length) ? _moduleEntries[index].DisplayName : null; + } + + internal int GetModuleStart(int index) { + return (index >= 0 && index < _moduleEntries.Length) ? _moduleEntries[index].Start : -1; + } + + internal int GetMethodCount(int moduleIndex) { + if (moduleIndex < 0 || moduleIndex >= _moduleEntries.Length) { + return 0; + } + + return _moduleEntries[moduleIndex].GetSortedMethods().Length; + } + + internal int GetMethodImageIndex(int moduleIndex, int methodIndex) { + if (moduleIndex < 0 || methodIndex < 0 || moduleIndex >= _moduleEntries.Length) { + return 0; + } + + var methods = _moduleEntries[moduleIndex].GetSortedMethods(); + return (methodIndex >= 0 && methodIndex < methods.Length) ? methods[methodIndex].ImageListIndex : 0; + } + + internal string GetMethodName(int moduleIndex, int methodIndex) { + if (moduleIndex < 0 || methodIndex < 0 || moduleIndex >= _moduleEntries.Length) { + return null; + } + + var methods = _moduleEntries[moduleIndex].GetSortedMethods(); + return (methodIndex < methods.Length) ? methods[methodIndex].DisplayName : null; + } + + internal int GetMethodStart(int moduleIndex, int methodIndex) { + if (moduleIndex < 0 || methodIndex < 0 || moduleIndex >= _moduleEntries.Length) { + return -1; + } + + var methods = _moduleEntries[moduleIndex].GetSortedMethods(); + return (methodIndex < methods.Length) ? methods[methodIndex].Start : -1; + } + } + + internal abstract class EntryBase { + private int _index; + private readonly string/*!*/ _displayName; + private readonly Node/*!*/ _definition; + + public EntryBase(string/*!*/ displayName, Node/*!*/ definition) { + Assert.NotNull(definition, displayName); + _displayName = displayName; + _definition = definition; + } + + public string/*!*/ DisplayName { + get { return _displayName; } + } + + public int Index { + get { return _index; } + set { _index = value; } + } + + internal bool Includes(int position) { + // top-level "entry" includes all positions: + return _definition.NodeType == NodeTypes.SourceUnitTree || position >= Start && position < End; + } + + public Node/*!*/ Definition { + get { return _definition; } + } + + public int Start { + get { return _definition.Location.Start.Index; } + } + + public int End { + get { return _definition.Location.End.Index; } + } + + public abstract int ImageListIndex { get; } + + /// + /// Turns an image list kind / overlay into the proper index in the image list. + /// + internal static int GetImageListIndex(ImageListKind kind, ImageListOverlay overlay) { + return ((int)kind) * 6 + (int)overlay; + } + } + + [DebuggerDisplay("{DisplayName}")] + internal sealed class ModuleEntry : EntryBase { + private readonly bool _isExpressionBound; + private readonly bool _isSingleton; + private readonly string/*!*/ _qualifiedName; + private List _nestedModules; + + // methods in the order as they appear in the AST (order by location): + private List _methods; + + // methods in the display order: + private MethodEntry[] _sortedMethods; + + public ModuleEntry(string/*!*/ displayName, string/*!*/ qualifiedName, Node/*!*/ definition, bool isExpressionBound, bool isSingleton) + : base(displayName, definition) { + _qualifiedName = qualifiedName; + _isExpressionBound = isExpressionBound; + _isSingleton = isSingleton; + } + + public bool IsExpressionBound { + get { return _isExpressionBound; } + } + + public bool IsSingleton { + get { return _isSingleton; } + } + + public string/*!*/ QualifiedName { + get { return _qualifiedName; } + } + + public void AddNested(ModuleEntry/*!*/ module) { + if (_nestedModules == null) { + _nestedModules = new List(); + } else { + // children need to be sorted by position: + Debug.Assert(_nestedModules.Last().End <= module.Start); + } + _nestedModules.Add(module); + } + + public ModuleEntry/*!*/ LocateModule(int position) { + // binary search nested entries: + int start = 0; + int end = _nestedModules != null ? _nestedModules.Count : 0; + while (start < end) { + int mid = (start + end) / 2; + var midEntry = _nestedModules[mid]; + if (position < midEntry.Start) { + end = mid; + } else if (position >= midEntry.End) { + start = mid + 1; + } else { + return midEntry.LocateModule(position); + } + } + return this; + } + + public MethodEntry LocateMethod(int position) { + return MethodEntry.LocateMethod(_methods, position); + } + + public void AddMethod(MethodEntry/*!*/ method) { + if (_methods == null) { + _methods = new List(); + } + _methods.Add(method); + } + + public MethodEntry[]/*!*/ GetSortedMethods() { + if (_sortedMethods == null) { + if (_methods != null) { + var array = _methods.ToArray(); + + Array.Sort(array, (x, y) => String.CompareOrdinal(x.DisplayName, y.DisplayName)); + for (int i = 0; i < array.Length; i++) { + array[i].Index = i; + } + + _sortedMethods = array; + } else { + _sortedMethods = MethodEntry.EmptyArray; + } + } + + return _sortedMethods; + } + + public override int ImageListIndex { + get { return GetImageListIndex(ImageListKind.Class, ImageListOverlay.ImageListOverlayNone); } + } + } + + [DebuggerDisplay("{DisplayName}")] + internal sealed class MethodEntry : EntryBase { + public static readonly MethodEntry[] EmptyArray = new MethodEntry[0]; + + private List _nestedMethods; + private readonly bool _isSingleton; + + public MethodEntry(MethodDefinition/*!*/ definition, bool isSingleton) + : base(definition.Name, definition) { + _isSingleton = isSingleton; + } + + public bool IsSingleton { + get { return _isSingleton; } + } + + public override int ImageListIndex { + get { return GetImageListIndex(_isSingleton ? ImageListKind.ClassMethod : ImageListKind.Method, ImageListOverlay.ImageListOverlayNone); } + } + + public void AddNested(MethodEntry/*!*/ method) { + if (_nestedMethods == null) { + _nestedMethods = new List(); + } else { + // children need to be sorted by position: + Debug.Assert(_nestedMethods.Last().End <= method.Start); + } + _nestedMethods.Add(method); + } + + public MethodEntry/*!*/ LocateMethod(int position) { + return LocateMethod(_nestedMethods, position) ?? this; + } + + public static MethodEntry LocateMethod(IList methods, int position) { + // binary search nested entries: + int start = 0; + int end = methods != null ? methods.Count : 0; + while (start < end) { + int mid = (start + end) / 2; + var midEntry = methods[mid]; + if (position < midEntry.Start) { + end = mid; + } else if (position >= midEntry.End) { + start = mid + 1; + } else { + return midEntry.LocateMethod(position); + } + } + return null; + } + } + + internal sealed class ModelBuilder : Walker { + private readonly SourceUnitTree/*!*/ _tree; + private readonly List/*!*/ _outerName; // stack + private readonly Stack/*!*/ _outerModules; + private readonly Stack/*!*/ _outerMethods; + private readonly Dictionary/*!*/ _definitions; + private readonly List/*!*/ _entries; + + public ModelBuilder(SourceUnitTree/*!*/ tree) { + _tree = tree; + var top = new ModuleEntry("
", "
", tree, false, false); + + _outerName = new List(); + _outerName.Add(null); + + _outerModules = new Stack(); + _outerModules.Push(top); + + _outerMethods = new Stack(); + + _definitions = new Dictionary(); + _entries = new List(); + _entries.Add(top); + } + + public Model/*!*/ Build() { + Walk(_tree); + Debug.Assert(_outerModules.Count == 1 && _outerName.Count == 1); + + var result = _entries.ToArray(); + + Array.Sort(result, (x, y) => + x.IsExpressionBound == y.IsExpressionBound ? String.CompareOrdinal(x.DisplayName, y.DisplayName) : x.IsExpressionBound ? +1 : -1 + ); + + for (int i = 0; i < result.Length; i++) { + result[i].Index = i; + } + + return new Model(_outerModules.Pop(), result); + } + + private static string/*!*/ BuildName(string prefix, ConstantVariable/*!*/ constant) { + var reversed = new List(); + ConstantVariable constantQualifier; + while (true) { + reversed.Add(constant.Name); + constantQualifier = constant.Qualifier as ConstantVariable; + if (constantQualifier == null) { + break; + } + constant = constantQualifier; + } + + // A::B + // ::A::B + // ::A::B + if (!constant.IsGlobal) { + if (constant.IsBound) { + reversed.Add(""); + } else if (prefix != null) { + reversed.Add(prefix); + } + } + + return String.Join("::", Enumerable.Reverse(reversed)); + } + + private bool IsSelfModuleReference(Expression target, out string moduleName) { + if (target == null) { + moduleName = null; + return false; + } + + if (target.NodeType == NodeTypes.SelfReference) { + moduleName = _outerModules.Peek().QualifiedName; + return true; + } + + if (target.NodeType == NodeTypes.ConstantVariable) { + string prefix = (_outerName.Count >= 2) ? _outerName[_outerName.Count - 2] : null; + moduleName = BuildName(prefix, (ConstantVariable)target); + return String.Equals(moduleName, _outerModules.Peek().QualifiedName, StringComparison.Ordinal); + } + + moduleName = ""; + return false; + } + + private void EnterModule(string/*!*/ name, Node/*!*/ definition, bool isExpressionBound, bool isSingleton) { + string displayName = name; + int count; + if (_definitions.TryGetValue(name, out count)) { + count++; + displayName = name + " (" + count + ")"; + } else { + count = 1; + } + _definitions[name] = count; + + var entry = new ModuleEntry(displayName, name, definition, isExpressionBound, isSingleton); + + _entries.Add(entry); + _outerModules.Peek().AddNested(entry); + _outerName.Add(name); + _outerModules.Push(entry); + + // add a boundary for method nesting: + _outerMethods.Push(null); + } + + private void ExitModule() { + _outerMethods.Pop(); + _outerModules.Pop(); + _outerName.RemoveAt(_outerName.Count - 1); + } + + public override bool Enter(ModuleDefinition/*!*/ node) { + var name = BuildName(_outerName.Last(), node.QualifiedName); + EnterModule(name, node, name[0] == '<', false); + return true; + } + + public override void Exit(ModuleDefinition/*!*/ node) { + ExitModule(); + } + + public override bool Enter(ClassDefinition/*!*/ node) { + return Enter((ModuleDefinition)node); + } + + public override void Exit(ClassDefinition/*!*/ node) { + Exit((ModuleDefinition)node); + } + + public override bool Enter(SingletonDefinition/*!*/ node) { + string name; + bool isSelfSingleton = IsSelfModuleReference(node.Singleton, out name); + Debug.Assert(name != null); + name += ".singleton"; + EnterModule(name, node, name[0] == '<', isSelfSingleton); + return true; + } + + public override void Exit(SingletonDefinition/*!*/ node) { + Exit((ModuleDefinition)node); + } + + public override bool Enter(MethodDefinition/*!*/ node) { + var outerModule = _outerModules.Peek(); + + string targetName; + bool isSelfSingleton = + !outerModule.IsSingleton && IsSelfModuleReference(node.Target, out targetName) || + outerModule.IsSingleton && node.Target == null; + + var entry = new MethodEntry(node, isSelfSingleton); + + // add to the outer method: + if (_outerMethods.Count > 0) { + var outerMethod = _outerMethods.Peek(); + if (outerMethod != null) { + outerMethod.AddNested(entry); + } + } + + // add to the outer module: + // TODO: lookup target name... + if (isSelfSingleton || node.Target == null) { + outerModule.AddMethod(entry); + } + + _outerName.Add(null); + _outerMethods.Push(entry); + return true; + } + + public override void Exit(MethodDefinition/*!*/ node) { + _outerName.RemoveAt(_outerName.Count - 1); + _outerMethods.Pop(); + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/RubyLanguageInfo.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/RubyLanguageInfo.cs new file mode 100644 index 0000000000..8d15851dcd --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/RubyLanguageInfo.cs @@ -0,0 +1,81 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.TextManager.Interop; + +namespace Microsoft.IronRubyTools.Navigation { + /// + /// Minimal language service. Implemented directly rather than using the Managed Package + /// Framework because we don't want to provide colorization services. Instead we use the + /// new Visual Studio 2010 APIs to provide these services. But we still need this to + /// provide a code window manager so that we can have a navigation bar (actually we don't, this + /// should be switched over to using our TextViewCreationListener instead). + /// + internal sealed class RubyLanguageInfo : IVsLanguageInfo { + private readonly IServiceProvider _serviceProvider; + private readonly IComponentModel _componentModel; + + public RubyLanguageInfo(IServiceProvider serviceProvider) { + _serviceProvider = serviceProvider; + _componentModel = serviceProvider.GetService(typeof(SComponentModel)) as IComponentModel; + } + + public int GetCodeWindowManager(IVsCodeWindow pCodeWin, out IVsCodeWindowManager ppCodeWinMgr) { + var model = _serviceProvider.GetService(typeof(SComponentModel)) as IComponentModel; + var service = model.GetService(); + + IVsTextView textView; + if (ErrorHandler.Succeeded(pCodeWin.GetPrimaryView(out textView))) { + ppCodeWinMgr = new CodeWindowManager(pCodeWin, service.GetWpfTextView(textView), _componentModel); + + return VSConstants.S_OK; + } + + ppCodeWinMgr = null; + return VSConstants.E_FAIL; + } + + public int GetFileExtensions(out string pbstrExtensions) { + // This is the same extension the language service was + // registered as supporting. + pbstrExtensions = ".rb"; + return VSConstants.S_OK; + } + + + public int GetLanguageName(out string bstrName) { + // This is the same name the language service was registered with. + bstrName = RubyConstants.LanguageName; + return VSConstants.S_OK; + } + + /// + /// GetColorizer is not implemented because we implement colorization using the new managed APIs. + /// + public int GetColorizer(IVsTextLines pBuffer, out IVsColorizer ppColorizer) { + ppColorizer = null; + return VSConstants.E_FAIL; + } + + public IServiceProvider ServiceProvider { + get { + return _serviceProvider; + } + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/RubyLibraryManager.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/RubyLibraryManager.cs new file mode 100644 index 0000000000..2332cc986e --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/RubyLibraryManager.cs @@ -0,0 +1,65 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using Microsoft.IronStudio.Navigation; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.IronRubyTools.Navigation { + + /// + /// This interface defines the service that finds IronRuby files inside a hierarchy + /// and builds the informations to expose to the class view or object browser. + /// + [Guid(RubyConstants.LibraryManagerServiceGuid)] + public interface IRubyLibraryManager : ILibraryManager { + } + + /// + /// Implementation of the service that builds the information to expose to the symbols + /// navigation tools (class view or object browser) from the Ruby files inside a + /// hierarchy. + /// + [Guid(RubyConstants.LibraryManagerGuid)] + internal class RubyLibraryManager : LibraryManager, IRubyLibraryManager { + private readonly IronRubyToolsPackage/*!*/ _package; + + public RubyLibraryManager(IronRubyToolsPackage/*!*/ package) + : base(package) { + _package = package; + } + + protected override LibraryNode CreateLibraryNode(IScopeNode subItem, string namePrefix, IVsHierarchy hierarchy, uint itemid) { + return new RubyLibraryNode(subItem, namePrefix, hierarchy, itemid); + } + + protected override void OnNewFile(LibraryTask task) { + //AnalysisItem item; + //if (task.TextBuffer != null) { + // item = task.TextBuffer.GetAnalysis(); + //} else { + // item = IronRubyToolsPackage.Instance.Analyzer.AnalyzeFile(task.FileName); + //} + + // We subscribe to OnNewAnalysis here instead of OnNewParseTree so that + // in the future we can use the analysis to include type information in the + // object browser (for example we could include base type information with + // links elsewhere in the object browser). + //item.OnNewAnalysis += (sender, args) => { + // FileParsed(task, new AstScopeNode(item.CurrentTree, item.Entry)); + //}; + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/RubyLibraryNode.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/RubyLibraryNode.cs new file mode 100644 index 0000000000..35d2a8f064 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Navigation/RubyLibraryNode.cs @@ -0,0 +1,112 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Text; +using IronRuby.Compiler.Ast; +using IronRuby.Runtime; +using Microsoft.IronStudio.Navigation; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.IronRubyTools.Navigation { + public class RubyLibraryNode : CommonLibraryNode { + public RubyLibraryNode(IScopeNode scope, string namePrefix, IVsHierarchy hierarchy, uint itemId) + : base(scope, namePrefix, hierarchy, itemId) { } + + protected RubyLibraryNode(RubyLibraryNode node) : base(node) { } + + protected override LibraryNode Clone() { + return new RubyLibraryNode(this); + } + + public override StandardGlyphGroup GlyphType { + get { + //if (ScopeNode is FunctionScopeNode) { + // return StandardGlyphGroup.GlyphGroupMethod; + //} + + return StandardGlyphGroup.GlyphGroupClass; + } + } + + public override string GetTextRepresentation(VSTREETEXTOPTIONS options) { + // TODO: + //FunctionScopeNode funcScope = ScopeNode as FunctionScopeNode; + //if (funcScope != null) { + // StringBuilder sb = new StringBuilder(); + // GetFunctionDescription(funcScope.Definition, (text, kind, arg) => { + // sb.Append(text); + // }); + // return sb.ToString(); + //} + + return Name; + } + + public override void FillDescription(_VSOBJDESCOPTIONS flags, IVsObjectBrowserDescription3 description) { + description.ClearDescriptionText(); + // TODO: + //FunctionScopeNode funcScope = ScopeNode as FunctionScopeNode; + //if (funcScope != null) { + // description.AddDescriptionText3("def ", VSOBDESCRIPTIONSECTION.OBDS_MISC, null); + // var def = funcScope.Definition; + // GetFunctionDescription(def, (text, kind, arg) => { + // description.AddDescriptionText3(text, kind, arg); + // }); + // description.AddDescriptionText3(null, VSOBDESCRIPTIONSECTION.OBDS_ENDDECL, null); + // if (def.Body.Documentation != null) { + // description.AddDescriptionText3(" " + def.Body.Documentation, VSOBDESCRIPTIONSECTION.OBDS_MISC, null); + // } + //} else { + description.AddDescriptionText3("class ", VSOBDESCRIPTIONSECTION.OBDS_MISC, null); + description.AddDescriptionText3(ScopeNode.Name, VSOBDESCRIPTIONSECTION.OBDS_NAME, null); + // } + } + + // TODO: + //private void GetFunctionDescription(FunctionDefinition def, Action addDescription) { + // addDescription(ScopeNode.Name, VSOBDESCRIPTIONSECTION.OBDS_NAME, null); + // addDescription("(", VSOBDESCRIPTIONSECTION.OBDS_MISC, null); + + // for (int i = 0; i < def.Parameters.Count; i++) { + // if (i != 0) { + // addDescription(", ", VSOBDESCRIPTIONSECTION.OBDS_MISC, null); + // } + + // var curParam = def.Parameters[i]; + + // string name = curParam.Name; + // if (curParam.IsDictionary) { + // name = "**" + name; + // } else if (curParam.IsList) { + // name = "*" + curParam.Name; + // } + + // if (curParam.DefaultValue != null) { + // // TODO: Support all possible expressions for default values, we should + // // probably have a RubyAst walker for expressions or we should add ToCodeString() + // // onto Ruby ASTs so they can round trip + // ConstantExpression defaultValue = curParam.DefaultValue as ConstantExpression; + // if (defaultValue != null) { + // name = name + " = " + RubyOps.Repr(DefaultContext.Default, defaultValue.Value); + // } + // } + + // addDescription(name, VSOBDESCRIPTIONSECTION.OBDS_PARAM, null); + // } + // addDescription(")\n", VSOBDESCRIPTIONSECTION.OBDS_MISC, null); + //} + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Options/RubyOptionsControl.Designer.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Options/RubyOptionsControl.Designer.cs new file mode 100644 index 0000000000..57d44916b2 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Options/RubyOptionsControl.Designer.cs @@ -0,0 +1,217 @@ +namespace Microsoft.IronRubyTools.Options { + partial class RubyOptionsControl { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Component Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this._editorGroup = new System.Windows.Forms.GroupBox(); + this._outliningOnOpen = new System.Windows.Forms.CheckBox(); + this._autoIndent = new System.Windows.Forms.CheckBox(); + this._intellisenseGroup = new System.Windows.Forms.GroupBox(); + this._enterCommits = new System.Windows.Forms.CheckBox(); + this._intersectMembers = new System.Windows.Forms.CheckBox(); + this._interactiveGroup = new System.Windows.Forms.GroupBox(); + this._smartReplHistory = new System.Windows.Forms.CheckBox(); + this._completionModeGroup = new System.Windows.Forms.GroupBox(); + this._evalAlways = new System.Windows.Forms.RadioButton(); + this._evalNoCalls = new System.Windows.Forms.RadioButton(); + this._evalNever = new System.Windows.Forms.RadioButton(); + this._editorGroup.SuspendLayout(); + this._intellisenseGroup.SuspendLayout(); + this._interactiveGroup.SuspendLayout(); + this._completionModeGroup.SuspendLayout(); + this.SuspendLayout(); + // + // _editorGroup + // + this._editorGroup.Controls.Add(this._outliningOnOpen); + this._editorGroup.Controls.Add(this._autoIndent); + this._editorGroup.Location = new System.Drawing.Point(3, 3); + this._editorGroup.Name = "_editorGroup"; + this._editorGroup.Size = new System.Drawing.Size(468, 67); + this._editorGroup.TabIndex = 0; + this._editorGroup.TabStop = false; + this._editorGroup.Text = "Editor"; + // + // _outliningOnOpen + // + this._outliningOnOpen.AutoSize = true; + this._outliningOnOpen.Location = new System.Drawing.Point(12, 20); + this._outliningOnOpen.Name = "_outliningOnOpen"; + this._outliningOnOpen.Size = new System.Drawing.Size(199, 17); + this._outliningOnOpen.TabIndex = 0; + this._outliningOnOpen.Text = "Enter &outlining mode when files open"; + this._outliningOnOpen.UseVisualStyleBackColor = true; + this._outliningOnOpen.CheckedChanged += new System.EventHandler(this._outliningOnOpen_CheckedChanged); + // + // _autoIndent + // + this._autoIndent.AutoSize = true; + this._autoIndent.Location = new System.Drawing.Point(12, 39); + this._autoIndent.Name = "_autoIndent"; + this._autoIndent.Size = new System.Drawing.Size(115, 17); + this._autoIndent.TabIndex = 10; + this._autoIndent.Text = "Enable &auto indent"; + this._autoIndent.UseVisualStyleBackColor = true; + this._autoIndent.CheckedChanged += new System.EventHandler(this._autoIndent_CheckedChanged); + // + // _intellisenseGroup + // + this._intellisenseGroup.Controls.Add(this._enterCommits); + this._intellisenseGroup.Controls.Add(this._intersectMembers); + this._intellisenseGroup.Location = new System.Drawing.Point(3, 76); + this._intellisenseGroup.Name = "_intellisenseGroup"; + this._intellisenseGroup.Size = new System.Drawing.Size(468, 61); + this._intellisenseGroup.TabIndex = 10; + this._intellisenseGroup.TabStop = false; + this._intellisenseGroup.Text = "Intellisense"; + this._intellisenseGroup.Visible = false; + // + // _enterCommits + // + this._enterCommits.AutoSize = true; + this._enterCommits.Location = new System.Drawing.Point(12, 20); + this._enterCommits.Name = "_enterCommits"; + this._enterCommits.Size = new System.Drawing.Size(182, 17); + this._enterCommits.TabIndex = 0; + this._enterCommits.Text = "&Enter commits current completion"; + this._enterCommits.UseVisualStyleBackColor = true; + this._enterCommits.CheckedChanged += new System.EventHandler(this._enterCommits_CheckedChanged); + // + // _intersectMembers + // + this._intersectMembers.AutoSize = true; + this._intersectMembers.Location = new System.Drawing.Point(12, 40); + this._intersectMembers.Name = "_intersectMembers"; + this._intersectMembers.Size = new System.Drawing.Size(272, 17); + this._intersectMembers.TabIndex = 10; + this._intersectMembers.Text = "Member completion displays &intersection of members"; + this._intersectMembers.UseVisualStyleBackColor = true; + this._intersectMembers.CheckedChanged += new System.EventHandler(this._intersectMembers_CheckedChanged); + // + // _interactiveGroup + // + this._interactiveGroup.Controls.Add(this._smartReplHistory); + this._interactiveGroup.Controls.Add(this._completionModeGroup); + this._interactiveGroup.Location = new System.Drawing.Point(6, 143); + this._interactiveGroup.Name = "_interactiveGroup"; + this._interactiveGroup.Size = new System.Drawing.Size(468, 142); + this._interactiveGroup.TabIndex = 20; + this._interactiveGroup.TabStop = false; + this._interactiveGroup.Text = "Interactive Window"; + // + // _smartReplHistory + // + this._smartReplHistory.AutoSize = true; + this._smartReplHistory.Location = new System.Drawing.Point(12, 20); + this._smartReplHistory.Name = "_smartReplHistory"; + this._smartReplHistory.Size = new System.Drawing.Size(210, 17); + this._smartReplHistory.TabIndex = 0; + this._smartReplHistory.Text = "Up/Down Arrow Keys use smart &history"; + this._smartReplHistory.UseVisualStyleBackColor = true; + this._smartReplHistory.CheckedChanged += new System.EventHandler(this._smartReplHistory_CheckedChanged); + // + // _completionModeGroup + // + this._completionModeGroup.Controls.Add(this._evalAlways); + this._completionModeGroup.Controls.Add(this._evalNoCalls); + this._completionModeGroup.Controls.Add(this._evalNever); + this._completionModeGroup.Location = new System.Drawing.Point(19, 44); + this._completionModeGroup.Name = "_completionModeGroup"; + this._completionModeGroup.Size = new System.Drawing.Size(434, 86); + this._completionModeGroup.TabIndex = 10; + this._completionModeGroup.TabStop = false; + this._completionModeGroup.Text = "Completion Mode"; + // + // _evalAlways + // + this._evalAlways.AutoSize = true; + this._evalAlways.Location = new System.Drawing.Point(12, 58); + this._evalAlways.Name = "_evalAlways"; + this._evalAlways.Size = new System.Drawing.Size(160, 17); + this._evalAlways.TabIndex = 20; + this._evalAlways.TabStop = true; + this._evalAlways.Text = "&Always evaluate expressions"; + this._evalAlways.UseVisualStyleBackColor = true; + this._evalAlways.CheckedChanged += new System.EventHandler(this._evalAlways_CheckedChanged); + // + // _evalNoCalls + // + this._evalNoCalls.AutoSize = true; + this._evalNoCalls.Location = new System.Drawing.Point(12, 39); + this._evalNoCalls.Name = "_evalNoCalls"; + this._evalNoCalls.Size = new System.Drawing.Size(232, 17); + this._evalNoCalls.TabIndex = 10; + this._evalNoCalls.TabStop = true; + this._evalNoCalls.Text = "Never evaluate expressions containing &calls"; + this._evalNoCalls.UseVisualStyleBackColor = true; + this._evalNoCalls.CheckedChanged += new System.EventHandler(this._evalNoCalls_CheckedChanged); + // + // _evalNever + // + this._evalNever.AutoSize = true; + this._evalNever.Location = new System.Drawing.Point(12, 20); + this._evalNever.Name = "_evalNever"; + this._evalNever.Size = new System.Drawing.Size(156, 17); + this._evalNever.TabIndex = 0; + this._evalNever.TabStop = true; + this._evalNever.Text = "&Never evaluate expressions"; + this._evalNever.UseVisualStyleBackColor = true; + this._evalNever.CheckedChanged += new System.EventHandler(this._evalNever_CheckedChanged); + // + // RubyOptionsControl + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.Controls.Add(this._editorGroup); + this.Controls.Add(this._intellisenseGroup); + this.Controls.Add(this._interactiveGroup); + this.Name = "RubyOptionsControl"; + this.Size = new System.Drawing.Size(474, 294); + this._editorGroup.ResumeLayout(false); + this._editorGroup.PerformLayout(); + this._intellisenseGroup.ResumeLayout(false); + this._intellisenseGroup.PerformLayout(); + this._interactiveGroup.ResumeLayout(false); + this._interactiveGroup.PerformLayout(); + this._completionModeGroup.ResumeLayout(false); + this._completionModeGroup.PerformLayout(); + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.GroupBox _editorGroup; + private System.Windows.Forms.GroupBox _intellisenseGroup; + private System.Windows.Forms.GroupBox _interactiveGroup; + private System.Windows.Forms.CheckBox _outliningOnOpen; + private System.Windows.Forms.CheckBox _autoIndent; + private System.Windows.Forms.CheckBox _enterCommits; + private System.Windows.Forms.CheckBox _intersectMembers; + private System.Windows.Forms.CheckBox _smartReplHistory; + private System.Windows.Forms.GroupBox _completionModeGroup; + private System.Windows.Forms.RadioButton _evalAlways; + private System.Windows.Forms.RadioButton _evalNoCalls; + private System.Windows.Forms.RadioButton _evalNever; + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Options/RubyOptionsControl.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Options/RubyOptionsControl.cs new file mode 100644 index 0000000000..ef7c88371d --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Options/RubyOptionsControl.cs @@ -0,0 +1,67 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Windows.Forms; +using Microsoft.IronStudio.Core.Repl; + +namespace Microsoft.IronRubyTools.Options { + public partial class RubyOptionsControl : UserControl { + public RubyOptionsControl() { + InitializeComponent(); + _outliningOnOpen.Checked = IronRubyToolsPackage.Instance.OptionsPage.EnterOutliningModeOnOpen; + _autoIndent.Checked = IronRubyToolsPackage.Instance.OptionsPage.AutoIndent; + _enterCommits.Checked = IronRubyToolsPackage.Instance.OptionsPage.EnterCommitsIntellisense; + _intersectMembers.Checked = IronRubyToolsPackage.Instance.OptionsPage.IntersectMembers; + _smartReplHistory.Checked = IronRubyToolsPackage.Instance.OptionsPage.ReplSmartHistory; + switch (IronRubyToolsPackage.Instance.OptionsPage.ReplIntellisenseMode) { + case ReplIntellisenseMode.AlwaysEvaluate: _evalAlways.Checked = true; break; + case ReplIntellisenseMode.DontEvaluateCalls: _evalNoCalls.Checked = true; break; + case ReplIntellisenseMode.NeverEvaluate: _evalNever.Checked = true; break; + } + } + + private void _outliningOnOpen_CheckedChanged(object sender, EventArgs e) { + IronRubyToolsPackage.Instance.OptionsPage.EnterOutliningModeOnOpen = _outliningOnOpen.Checked; + } + + private void _autoIndent_CheckedChanged(object sender, EventArgs e) { + IronRubyToolsPackage.Instance.OptionsPage.AutoIndent = _autoIndent.Checked; + } + + private void _enterCommits_CheckedChanged(object sender, EventArgs e) { + IronRubyToolsPackage.Instance.OptionsPage.EnterCommitsIntellisense = _enterCommits.Checked; + } + + private void _intersectMembers_CheckedChanged(object sender, EventArgs e) { + IronRubyToolsPackage.Instance.OptionsPage.IntersectMembers = _intersectMembers.Checked; + } + + private void _smartReplHistory_CheckedChanged(object sender, EventArgs e) { + IronRubyToolsPackage.Instance.OptionsPage.ReplSmartHistory = _smartReplHistory.Checked; + } + + private void _evalNever_CheckedChanged(object sender, EventArgs e) { + IronRubyToolsPackage.Instance.OptionsPage.ReplIntellisenseMode = ReplIntellisenseMode.NeverEvaluate; + } + + private void _evalNoCalls_CheckedChanged(object sender, EventArgs e) { + IronRubyToolsPackage.Instance.OptionsPage.ReplIntellisenseMode = ReplIntellisenseMode.DontEvaluateCalls; + } + + private void _evalAlways_CheckedChanged(object sender, EventArgs e) { + IronRubyToolsPackage.Instance.OptionsPage.ReplIntellisenseMode = ReplIntellisenseMode.AlwaysEvaluate; + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Options/RubyOptionsControl.resx b/Tools/IronStudio/IronRubyTools/IronRubyTools/Options/RubyOptionsControl.resx new file mode 100644 index 0000000000..29dcb1b3a3 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Options/RubyOptionsControl.resx @@ -0,0 +1,120 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Options/RubyOptionsPage.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Options/RubyOptionsPage.cs new file mode 100644 index 0000000000..07cacf9bfb --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Options/RubyOptionsPage.cs @@ -0,0 +1,101 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel; +using Microsoft.VisualStudio.Shell; +using Microsoft.IronRubyTools.Commands; +using Microsoft.IronStudio.Core.Repl; + +namespace Microsoft.IronRubyTools.Options { + class RubyOptionsPage : DialogPage { + private bool _noEnterCommitsIntellisense, _noEnterOutliningMode, _noAutoIndent, _noIntersectMembers, _noSmartHistory; + private ReplIntellisenseMode _replIntellisenseMode; + private RubyOptionsControl _window; + + // replace the default UI of the dialog page w/ our own UI. + protected override System.Windows.Forms.IWin32Window Window { + get { + if (_window == null) { + _window = new RubyOptionsControl(); + } + return _window; + } + } + + #region Editor Options + + public bool EnterOutliningModeOnOpen { + get { return !_noEnterOutliningMode; } + set { + _noEnterOutliningMode = !value; + + IronRubyToolsPackage.Instance.RuntimeHost.EnterOutliningModeOnOpen = EnterOutliningModeOnOpen; + } + } + + public bool AutoIndent { get { return !_noAutoIndent; } set { _noAutoIndent = !value; } } + + #endregion + + #region Repl Options + + public bool ReplSmartHistory { + get { return !_noSmartHistory; } + set { + _noSmartHistory = !value; + + // propagate changes + var repl = ExecuteInReplCommand.TryGetReplWindow(); + if (repl != null) { + repl.UseSmartUpDown = ReplSmartHistory; + } + } + } + + public ReplIntellisenseMode ReplIntellisenseMode { + get { return _replIntellisenseMode; } + set { _replIntellisenseMode = value; } + } + + #endregion + + #region Intellisense Options + + public bool EnterCommitsIntellisense { + get { return !_noEnterCommitsIntellisense; } + set { _noEnterCommitsIntellisense = !value; } + } + + public bool IntersectMembers { + get { return !_noIntersectMembers; } + set { + _noIntersectMembers = !value; +#if FEATURE_INTELLISENSE + IronRubyToolsPackage.Instance.RuntimeHost.IntersectMembers = IntersectMembers; +#endif + } + } + + #endregion + + public override void ResetSettings() { + _noEnterCommitsIntellisense = false; + _noEnterOutliningMode = false; + _noAutoIndent = false; + _noIntersectMembers = false; + _noSmartHistory = false; + _replIntellisenseMode = ReplIntellisenseMode.DontEvaluateCalls; + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/IRubyStarter.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/IRubyStarter.cs new file mode 100644 index 0000000000..6ab75f73bc --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/IRubyStarter.cs @@ -0,0 +1,24 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + + +using Microsoft.IronStudio.Project; + +namespace Microsoft.IronRubyTools.Navigation { + /// + /// This is a marker interface required to query Ruby starter + /// service proffered by Ruby project system package. + /// + public interface IRubyStarter : IStarter {} +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyEditorFactory.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyEditorFactory.cs new file mode 100644 index 0000000000..34f51a3626 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyEditorFactory.cs @@ -0,0 +1,31 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using Microsoft.IronStudio.Project; + + +namespace Microsoft.IronRubyTools.Project { + /// + /// Factory for creating code editor. + /// + /// + /// While currently empty, editor factory has to be unique per language. + /// + [Guid(RubyConstants.EditorFactoryGuid)] + public class RubyEditorFactory : CommonEditorFactory { + public RubyEditorFactory(CommonProjectPackage package) : base(package) { } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyFileNode.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyFileNode.cs new file mode 100644 index 0000000000..ada3838330 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyFileNode.cs @@ -0,0 +1,28 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +// + +using Microsoft.IronRubyTools.Navigation; +using Microsoft.IronStudio.Project; +using Microsoft.VisualStudio.Project; + + +namespace Microsoft.IronRubyTools.Project { + + public class RubyFileNode : CommonFileNode { + internal RubyFileNode(CommonProjectNode root, ProjectElement e) + : base(root, e) { } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyGeneralPropertyPage.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyGeneralPropertyPage.cs new file mode 100644 index 0000000000..d37bfffcfa --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyGeneralPropertyPage.cs @@ -0,0 +1,33 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + + +using System; +using System.Runtime.InteropServices; +using Microsoft.IronStudio.Project; + + +namespace Microsoft.IronRubyTools.Project { + /// + /// "General" tab in project properties. + /// + /// While currently empty this class has to be in this + /// assembly and has to have unique guid. + /// In the future if we need to add new properties to this page + /// this is the place where it can be done. + [Guid(RubyConstants.GeneralPropertyPageGuid)] + public class RubyGeneralPropertyPage : CommonGeneralPropertyPage { + + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyProjectFactory.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyProjectFactory.cs new file mode 100644 index 0000000000..ae382ac457 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyProjectFactory.cs @@ -0,0 +1,136 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.Project; +using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; +using System.Diagnostics; +using System.Windows.Forms; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio; +using Microsoft.IronStudio.Utils; +using System.ComponentModel; +using System.Threading; + +namespace Microsoft.IronRubyTools.Project { + /// + /// Creates Ruby Projects + /// + [Guid(RubyConstants.ProjectFactoryGuid)] + public class RubyProjectFactory : ProjectFactory { + + public RubyProjectFactory(RubyProjectPackage/*!*/ package) + : base(package) { + } + + protected override ProjectNode/*!*/ CreateProject() { + RubyProjectNode project = new RubyProjectNode((RubyProjectPackage)Package); + project.SetSite((IOleServiceProvider)((IServiceProvider)this.Package).GetService(typeof(IOleServiceProvider))); + return project; + } + + // filename: .rbproj file in temp + // location: user selected target location of the project + protected override void CreateProject(string fileName, string location, string name, uint flags, ref Guid projectGuid, out IntPtr project, out int canceled) { + string tempDir = Path.GetDirectoryName(fileName); + + // TODO: gem checks should not need a script + string script = Path.GetFullPath(Path.Combine(tempDir, "__Gems.rb")); + bool isGemLoader = File.Exists(script); + if (!isGemLoader) { + script = Path.GetFullPath(Path.Combine(tempDir, "__TemplateScript.rb")); + if (!File.Exists(script)) { + script = null; + } + } + + if (script != null) { + IVsOutputWindowPane outputPane = RubyVsUtils.GetOrCreateOutputPane("Project", RubyConstants.ProjectOutputPaneGuid); + if (outputPane != null) { + RubyVsUtils.ShowWindow(EnvDTE.Constants.vsWindowKindOutput); + outputPane.Activate(); + } + + if (!IronRubyToolsPackage.Instance.RequireIronRubyInstalled(allowCancel: true)) { + canceled = 1; + project = IntPtr.Zero; + return; + } + + var dir = Path.GetFullPath(Path.Combine(location, "..")); + var cancelled = new ManualResetEvent(false); + + ProgressForm progressForm = new ProgressForm(10); + progressForm.Text = isGemLoader ? "Checking required Gems" : "Creating Web Application"; + progressForm.CancellationEvent = cancelled; + + bool success = false; + + progressForm.BackgroundWorker.DoWork += new DoWorkEventHandler((s, e) => { + success = RubyStarter.ExecuteScriptFile(script, dir, outputPane, progressForm.BackgroundWorker, cancelled); + }); + + progressForm.BackgroundWorker.RunWorkerAsync(); + progressForm.ShowDialog(); + + File.Delete(script); + + if (success) { + if (!isGemLoader) { + try { + CopyFilesRecursive(tempDir, location); + } catch { + Rollback(location); + throw; + } + } + } else { + Rollback(location); + + if (progressForm.Cancelled) { + outputPane.OutputStringThreadSafe("Cancelled."); + } else { + MessageBox.Show( + "An error occured while creating project. See output window for details.", + "Error", + MessageBoxButtons.OK, + MessageBoxIcon.Error + ); + } + + canceled = 1; + project = IntPtr.Zero; + return; + } + } + + base.CreateProject(fileName, location, name, flags, ref projectGuid, out project, out canceled); + } + + private static void Rollback(string/*!*/ targetDir) { + Directory.Delete(targetDir, true); + } + + private static void CopyFilesRecursive(string/*!*/ sourceDir, string/*!*/ targetDir) { + foreach (string src in Directory.EnumerateFiles(sourceDir, "*", SearchOption.AllDirectories)) { + Debug.Assert(src.StartsWith(sourceDir + "\\")); + string dst = Path.Combine(targetDir, src.Substring(sourceDir.Length + 1)); + File.Copy(src, dst, true); + } + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyProjectNode.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyProjectNode.cs new file mode 100644 index 0000000000..853527d363 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyProjectNode.cs @@ -0,0 +1,66 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.IronRubyTools.Navigation; +using Microsoft.IronStudio.Project; +using Microsoft.VisualStudio.Project; + +namespace Microsoft.IronRubyTools.Project { + [Guid(RubyConstants.ProjectNodeGuid)] + public class RubyProjectNode : DirectoryBasedProjectNode { + public RubyProjectNode(RubyProjectPackage/*!*/ package) + : base(package, Utilities.GetImageList(typeof(RubyProjectNode).Assembly.GetManifestResourceStream(RubyConstants.ProjectImageList))) { + } + + public override Type GetProjectFactoryType() { + return typeof(RubyProjectFactory); + } + + public override string GetProjectName() { + return "RubyProject"; + } + + public override string GetCodeFileExtension() { + return RubyConstants.FileExtension; + } + + public override string GetFormatList() { + return String.Format(CultureInfo.CurrentCulture, ".rb"/*Resources.ProjectFileExtensionFilter*/, "\0", "\0"); + } + + public override Type GetGeneralPropertyPageType() { + return typeof(RubyGeneralPropertyPage); + } + + public override Type GetEditorFactoryType() { + return typeof(RubyEditorFactory); + } + + public override Type GetLibraryManagerType() { + return typeof(IRubyLibraryManager); + } + + public override string GetProjectFileExtension() { + return ".rbproj"; + } + + public override FolderNode CreateFolderNode(string absFolderPath) { + return base.CreateFolderNode(absFolderPath); + } + } +} \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyProjectPackage.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyProjectPackage.cs new file mode 100644 index 0000000000..c664ac16cb --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyProjectPackage.cs @@ -0,0 +1,98 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using Microsoft.IronRubyTools.Navigation; +using Microsoft.IronStudio.Project; +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio.Shell; + +namespace Microsoft.IronRubyTools.Project { + [PackageRegistration(UseManagedResourcesOnly = true)] + //Set the projectsTemplatesDirectory to a non-existant path to prevent VS from including the working directory as a valid template path + [ProvideProjectFactory(typeof(RubyProjectFactory), RubyConstants.LanguageName, + "IronRuby Project Files (*.rbproj);*.rbproj", "rbproj", "rbproj", "non-existant path", LanguageVsTemplate = RubyConstants.LanguageName)] + //[SingleFileGeneratorSupportRegistration(typeof(RubyProjectFactory))] + [ProvideObject(typeof(RubyGeneralPropertyPage))] + [ProvideEditorExtension(typeof(RubyEditorFactory), ".rb", 32)] + [ProvideEditorExtension(typeof(RubyEditorFactory), ".ru", 32)] + [ProvideEditorExtension(typeof(RubyEditorFactory), ".gemspec", 32)] + [ProvideEditorLogicalView(typeof(RubyEditorFactory), "{7651a702-06e5-11d1-8ebd-00a0c90f26ea}")] //LOGVIEWID_Designer + [ProvideEditorLogicalView(typeof(RubyEditorFactory), "{7651a701-06e5-11d1-8ebd-00a0c90f26ea}")] //LOGVIEWID_Code + /*[ProvideLoadKey(CommonConstants.PLKMinEdition, RubyProjectConstants.PLKProductVersion, + RubyProjectConstants.PLKProductName, CommonConstants.PLKCompanyName, CommonConstants.PLKResourceID)]*/ + [Guid(RubyConstants.ProjectSystemPackageGuid)] + //[WebSiteProject(RubyProjectConstants.IronRubyLanguageName, RubyProjectConstants.IronRubyLanguageName)] + [ProvideService(typeof(IRubyStarter))] + //[ProvideAutoLoad("ADFC4E64-0397-11D1-9F4E-00A0C911004F")] // load w/ and w/o a solution so we can initialize our filetypes + //[ProvideAutoLoad("f1536ef8-92ec-443c-9ed7-fdadf150da82")] + public class RubyProjectPackage : CommonProjectPackage { + public override ProjectFactory CreateProjectFactory() { + return new RubyProjectFactory(this); + } + + public override CommonEditorFactory CreateEditorFactory() { + return new RubyEditorFactory(this); + } + + /// + /// This method is called to get the icon that will be displayed in the + /// Help About dialog when this package is selected. + /// + /// The resource id corresponding to the icon to display on the Help About dialog + public override uint GetIconIdForAboutBox() { + return RubyConstants.IconIdForAboutBox; + } + /// + /// This method is called during Devenv /Setup to get the bitmap to + /// display on the splash screen for this package. + /// + /// The resource id corresponding to the bitmap to display on the splash screen + public override uint GetIconIdForSplashScreen() { + return RubyConstants.IconIfForSplashScreen; + } + /// + /// This methods provides the product official name, it will be + /// displayed in the help about dialog. + /// + public override string GetProductName() { + return RubyConstants.LanguageName; + //return Resources.ProductName; + } + /// + /// This methods provides the product description, it will be + /// displayed in the help about dialog. + /// + public override string GetProductDescription() { + return "IronRuby"; + //return Resources.ProductDescription; + } + /// + /// This methods provides the product version, it will be + /// displayed in the help about dialog. + /// + public override string GetProductVersion() { + return this.GetType().Assembly.GetName().Version.ToString(); + } + + /// + /// Creates an instance of a language specific service that + /// allows to start a project or a file with or without debugging. + /// + protected override IStarter/*!*/ CreateStarter() { + return new RubyStarter((IServiceProvider)this); + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyStarter.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyStarter.cs new file mode 100644 index 0000000000..9fbcaf7846 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Project/RubyStarter.cs @@ -0,0 +1,271 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Specialized; +using System.IO; +using System.Reflection; +using Microsoft.IronRubyTools.Navigation; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Project; +using System.Text; +using IronRuby.Runtime; +using System.Diagnostics; +using Microsoft.VisualStudio.Shell.Interop; +using System.Threading; +using Microsoft.Scripting.Utils; +using System.Net.Sockets; +using Microsoft.Win32.SafeHandles; +using System.ComponentModel; + +namespace Microsoft.IronRubyTools.Project { + /// + /// Provides Ruby specific functionality for starting a project or a file with or without debugging. + /// + public sealed class RubyStarter : CommonStarter, IRubyStarter { + public RubyStarter(IServiceProvider/*!*/ serviceProvider) + : base(serviceProvider) { + } + + private string/*!*/ CreateCommandLine(CommonProjectNode project, string/*!*/ startupFile, bool debug) { + var commandLine = new StringBuilder(); + + bool disableDebugging = project != null && Convert.ToBoolean(project.GetProjectProperty(RubyConstants.ProjectProperties.DisableDebugging, true)); + if (debug && !disableDebugging) { + commandLine.Append(" -D"); + } + + string searchPath = (project != null) ? project.GetProjectProperty(CommonConstants.SearchPath, true) : null; + if (!String.IsNullOrEmpty(searchPath)) { + foreach (string path in searchPath.Split(Path.PathSeparator)) { + try { + // the path is relative to the .rbproj file, not to the ir.exe file: + var fullPath = RubyUtils.CanonicalizePath(Path.GetFullPath(path)); + + commandLine.Append(" -I"); + if (fullPath.IndexOf(' ') >= 0) { + commandLine.Append('"').Append(fullPath).Append('"'); + } else { + commandLine.Append(path); + } + } catch { + // ignore + } + } + } + + commandLine.Append(' ').Append('"').Append(startupFile).Append('"'); + + string args = null; + string launcher = (project != null) ? project.GetProjectProperty(RubyConstants.ProjectProperties.Launcher, true) : null; + if (launcher == RubyConstants.ProjectProperties.Launcher_Spec) { + string testDir = Path.GetFullPath(Path.Combine(project.GetWorkingDirectory(), "test")); + if (Directory.Exists(testDir)) { + args = String.Join(" ", + Directory.GetFiles(testDir, "*_spec.rb").ConvertAll((path) => path.IndexOf(' ') >= 0 ? '"' + path + '"' : path) + ); + } + } else { + args = (project != null) ? project.GetProjectProperty(CommonConstants.CommandLineArguments, true) : null; + } + + if (!String.IsNullOrEmpty(args)) { + commandLine.Append(' '); + commandLine.Append(args); + } + + return commandLine.ToString(); + } + + public override string GetInterpreterExecutable(CommonProjectNode project) { + return project != null && Convert.ToBoolean(project.GetProjectProperty(CommonConstants.IsWindowsApplication, true)) ? + IronRubyToolsPackage.Instance.IronRubyWindowsExecutable : + IronRubyToolsPackage.Instance.IronRubyExecutable; + } + + + public override void StartProject(CommonProjectNode project, bool debug) { + IronRubyToolsPackage.Instance.RequireIronRubyInstalled(allowCancel: false); + + string launcher = (project != null) ? project.GetProjectProperty(RubyConstants.ProjectProperties.Launcher, true) : null; + string file; + switch (launcher) { + case RubyConstants.ProjectProperties.Launcher_Rack: + file = Path.Combine(IronRubyToolsPackage.Instance.IronRubyToolsPath, "Rackup.rb"); + break; + + case RubyConstants.ProjectProperties.Launcher_Spec: + file = Path.Combine(IronRubyToolsPackage.Instance.IronRubyToolsPath, "Spec.rb"); + break; + + default: + file = project.GetStartupFile(); + if (String.IsNullOrEmpty(file)) { + //TODO: need to start active file then + throw new ApplicationException("No startup file is defined for the startup project."); + } + break; + } + + StartFile(project, file, debug); + } + + public override void StartFile(CommonProjectNode project, string/*!*/ file, bool debug) { + string appType = (project != null) ? project.GetProjectProperty(RubyConstants.ProjectProperties.RubyApplicationType, true) : null; + if (appType == RubyConstants.ProjectProperties.RubyApplicationType_WebApp) { + string host = "localhost"; + int port = Convert.ToInt32(project.GetProjectProperty(RubyConstants.ProjectProperties.DefaultPort, true)); + if (LaunchWebServer(project, file, host, port)) { + StartInBrowser("http://" + host + ":" + port, null); + } else { + throw new ApplicationException(String.Format( + "Unable to start a web server by running file '{0}'. No response on http://{1}:{2}.", + file, host, port + )); + } + } else { + base.StartFile(project, file, debug); + } + } + + public override string CreateCommandLineNoDebug(CommonProjectNode project, string/*!*/ startupFile) { + return CreateCommandLine(project, startupFile, false); + } + + public override string CreateCommandLineDebug(CommonProjectNode project, string/*!*/ startupFile) { + return CreateCommandLine(project, startupFile, true); + } + + private bool LaunchWebServer(CommonProjectNode/*!*/ project, string/*!*/ file, string/*!*/ host, int port) { + bool serverLaunched = false; + + Socket socket; + socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp); + + int time = 0; + while (time < 50000) { + try { + socket.Connect(host, port); + socket.Disconnect(false); + return true; + } catch (SocketException e) { + if (e.SocketErrorCode != SocketError.ConnectionRefused) { + throw; + } + if (!serverLaunched) { + Process.Start(CreateProcessStartInfoNoDebug(project, file)); + serverLaunched = true; + } + Thread.Sleep(500); + time += 500; + } + } + + return false; + } + + internal static bool ExecuteScriptFile(string/*!*/ scriptFile, string/*!*/ workingDirectory, + IVsOutputWindowPane/*!*/ output, BackgroundWorker/*!*/ worker, ManualResetEvent/*!*/ cancelled) { + + if (scriptFile.IndexOf(' ') >= 0) { + scriptFile = '"' + scriptFile + '"'; + } + + ProcessStartInfo startInfo = new ProcessStartInfo(IronRubyToolsPackage.Instance.IronRubyExecutable, scriptFile); + startInfo.WorkingDirectory = workingDirectory; + startInfo.CreateNoWindow = true; + return ExecuteProcess(startInfo, output, worker, cancelled) == 0; + } + + private static int ExecuteProcess(ProcessStartInfo/*!*/ startInfo, + IVsOutputWindowPane/*!*/ output, BackgroundWorker/*!*/ worker, ManualResetEvent/*!*/ cancelled) { + + Assert.NotNull(startInfo, output); + Process process; + startInfo.UseShellExecute = false; + startInfo.RedirectStandardOutput = true; + startInfo.RedirectStandardError = true; + process = Process.Start(startInfo); + + WaitHandle stdErr = RedirectOutput(process, process.StandardError, output, worker); + WaitHandle stdOut = RedirectOutput(process, process.StandardOutput, output, worker); + + bool result = Wait(process, cancelled); + stdErr.WaitOne(); + stdOut.WaitOne(); + return result ? process.ExitCode : -1; + } + + private static bool Wait(Process/*!*/ process, WaitHandle/*!*/ cancelled) { + switch (WaitHandle.WaitAny(new[] { process.GetWaitHandle(), cancelled })) { + case 0: + return true; + + case 1: + // cancelled: + process.Kill(); + return false; + + default: + throw Assert.Unreachable; + } + } + + private static void StreamOutput(Process/*!*/ process, StreamReader/*!*/ from, IVsOutputWindowPane/*!*/ output, BackgroundWorker worker) { + while (!process.HasExited) { + string line; + while ((line = from.ReadLine()) != null) { + output.OutputStringThreadSafe(line); + output.OutputStringThreadSafe("\n"); + worker.ReportProgress(5); + } + } + } + + private static WaitHandle/*!*/ RedirectOutput(Process/*!*/ process, StreamReader/*!*/ from, IVsOutputWindowPane/*!*/ output, BackgroundWorker worker) { + var done = new ManualResetEvent(false); + + ThreadPool.QueueUserWorkItem(new WaitCallback((_) => { + StreamOutput(process, from, output, worker); + done.Set(); + }), null); + + return done; + } + + public override string ChironPath { + get { + string result; +#if DEBUG + result = Environment.GetEnvironmentVariable("DLR_ROOT"); + if (result != null) { + result = Path.Combine(result, @"Bin\Silveright4Debug\Chiron.exe"); + if (File.Exists(result)) { + return result; + } + } +#endif + result = IronRubyToolsPackage.Instance.IronRubyBinPath; + if (result != null) { + result = Path.Combine(result, @"..\Silverlight\bin\Chiron.exe"); + if (File.Exists(result)) { + return result; + } + } + + return base.ChironPath; + } + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Repl/RemoteRubyVsEvaluator.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Repl/RemoteRubyVsEvaluator.cs new file mode 100644 index 0000000000..681419d305 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Repl/RemoteRubyVsEvaluator.cs @@ -0,0 +1,106 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.IronRubyTools.Library.Repl; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Core.Repl; +using Microsoft.Scripting.Hosting; +using System.IO; +using IronRuby; +using Microsoft.Scripting; +using IronRuby.Runtime; +using System.Collections.Generic; +using System.Windows; +using Microsoft.Scripting.Utils; + +namespace Microsoft.IronRubyTools.Repl { + internal sealed class RemoteRubyVsEvaluator : RemoteRubyEvaluator { + // Constructed via reflection when deserialized from the registry. + public RemoteRubyVsEvaluator() { + } + + public override void Reset() { + base.Reset(); + Initialize(); + } + + public void Initialize() { + string filename, dir; + if (CommonPackage.TryGetStartupFileAndDirectory(out filename, out dir)) { + RemoteScriptFactory.SetCurrentDirectory(dir); + } + } + + protected override bool ShouldEvaluateForCompletion(string source) { + switch (IronRubyToolsPackage.Instance.OptionsPage.ReplIntellisenseMode) { + case ReplIntellisenseMode.AlwaysEvaluate: return true; + case ReplIntellisenseMode.DontEvaluateCalls: return base.ShouldEvaluateForCompletion(source); + case ReplIntellisenseMode.NeverEvaluate: return false; + default: throw new InvalidOperationException(); + } + } + + private static LanguageSetup GetSetupByName(ScriptRuntimeSetup/*!*/ setup, string/*!*/ name) { + foreach (var languageSetup in setup.LanguageSetups) { + if (languageSetup.Names.IndexOf(name) >= 0) { + return languageSetup; + } + } + return null; + } + + private static IList/*!*/ NormalizePaths(string/*!*/ dir, IList paths) { + if (paths == null) { + return ArrayUtils.EmptyStrings; + } + + var result = new List(paths.Count); + foreach (string path in paths) { + try { + result.Add(Path.GetFullPath(Path.Combine(dir, path))); + } catch { + // nop + } + } + return result; + } + + protected override ScriptRuntime/*!*/ CreateRuntime() { + string root = IronRubyToolsPackage.Instance.IronRubyBinPath; + string configPath = IronRubyToolsPackage.Instance.IronRubyExecutable + ".config"; + LanguageSetup existingRubySetup = null; + LanguageSetup rubySetup = Ruby.CreateRubySetup(); + + if (File.Exists(configPath)) { + try { + existingRubySetup = GetSetupByName(ScriptRuntimeSetup.ReadConfiguration(configPath), "IronRuby"); + } catch { + // TODO: report the error + } + } + + if (existingRubySetup != null) { + var options = new RubyOptions(existingRubySetup.Options); + rubySetup.Options["LibraryPaths"] = NormalizePaths(root, options.LibraryPaths); + rubySetup.Options["RequiredPaths"] = NormalizePaths(root, options.RequirePaths); + rubySetup.Options["SearchPaths"] = NormalizePaths(root, options.SearchPaths); + } + + var runtimeSetup = new ScriptRuntimeSetup(); + runtimeSetup.LanguageSetups.Add(rubySetup); + return RemoteScriptFactory.CreateRuntime(runtimeSetup); + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/Repl/RubyVsEvaluator.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/Repl/RubyVsEvaluator.cs new file mode 100644 index 0000000000..05bd88bb2c --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/Repl/RubyVsEvaluator.cs @@ -0,0 +1,41 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.IronRubyTools.Library.Repl; +using Microsoft.Scripting.Hosting; +using Microsoft.IronStudio.Core.Repl; + +namespace Microsoft.IronRubyTools.Repl { + internal sealed class RubyVsEvaluator : RubyEvaluator { + public RubyVsEvaluator() { + } + + public override void PublishScopeVariables(ScriptScope scope) { + scope.SetVariable("DTE", IronRubyToolsPackage.Instance.DTE); + } + + protected override bool ShouldEvaluateForCompletion(string source) { + switch (IronRubyToolsPackage.Instance.OptionsPage.ReplIntellisenseMode) { + case ReplIntellisenseMode.AlwaysEvaluate: return true; + case ReplIntellisenseMode.DontEvaluateCalls: return base.ShouldEvaluateForCompletion(source); + case ReplIntellisenseMode.NeverEvaluate: return false; + default: throw new InvalidOperationException(); + } + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/RubyVsUtils.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/RubyVsUtils.cs new file mode 100644 index 0000000000..3d6c72c1f3 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/RubyVsUtils.cs @@ -0,0 +1,97 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Threading; +using Microsoft.IronRubyTools.Intellisense; +using Microsoft.IronRubyTools.Project; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.Win32.SafeHandles; +using Microsoft.IronStudio; + +namespace Microsoft.IronRubyTools { + internal static class RubyVsUtils { + private sealed class GenericWaitHandle : WaitHandle { + public GenericWaitHandle(IntPtr handle) { + SafeWaitHandle = new SafeWaitHandle(handle, false); + } + } + + internal static WaitHandle/*!*/ GetWaitHandle(this Process/*!*/ process) { + return new GenericWaitHandle(process.Handle); + } + + internal static RubyProjectNode GetRubyProject(this EnvDTE.Project project) { + return project.GetCommonProject() as RubyProjectNode; + } + + internal static IVsOutputWindowPane GetOrCreateOutputPane(string/*!*/ name, Guid guid, bool clearWithSolution = false) { + IVsOutputWindowPane outputPane = null; + IVsOutputWindow output = (IVsOutputWindow)Package.GetGlobalService(typeof(SVsOutputWindow)); + + if (ErrorHandler.Failed(output.GetPane(ref guid, out outputPane))) { + if (!ErrorHandler.Failed(output.CreatePane(ref guid, name, Convert.ToInt32(true), Convert.ToInt32(clearWithSolution)))) { + output.GetPane(ref guid, out outputPane); + } + } + + Debug.Assert(outputPane != null); + return outputPane; + } + + internal static void ShowWindow(object/*!*/ windowGuid) { + var dte = (EnvDTE.DTE)Package.GetGlobalService(typeof(EnvDTE.DTE)); + var outputWindow = dte.Windows.Item(windowGuid); + Debug.Assert(outputWindow != null, "Unknown window: " + windowGuid); + outputWindow.Visible = true; + } + + internal static void GotoSource(this LocationInfo location) { + IronRubyToolsPackage.NavigateTo( + location.FilePath, + Guid.Empty, + location.Line - 1, + location.Column - 1); + } + +#if FEATURE_INTELLISENSE + internal static StandardGlyphGroup ToGlyphGroup(this ObjectType objectType) { + StandardGlyphGroup group; + switch (objectType) { + case ObjectType.Class: group = StandardGlyphGroup.GlyphGroupClass; break; + case ObjectType.Delegate: group = StandardGlyphGroup.GlyphGroupDelegate; break; + case ObjectType.Enum: group = StandardGlyphGroup.GlyphGroupEnum; break; + case ObjectType.Namespace: group = StandardGlyphGroup.GlyphGroupNamespace; break; + case ObjectType.Multiple: group = StandardGlyphGroup.GlyphGroupOverload; break; + case ObjectType.Field: group = StandardGlyphGroup.GlyphGroupField; break; + case ObjectType.Module: group = StandardGlyphGroup.GlyphGroupModule; break; + case ObjectType.Property: group = StandardGlyphGroup.GlyphGroupProperty; break; + case ObjectType.Instance: group = StandardGlyphGroup.GlyphGroupVariable; break; + case ObjectType.Constant: group = StandardGlyphGroup.GlyphGroupConstant; break; + case ObjectType.EnumMember: group = StandardGlyphGroup.GlyphGroupEnumMember; break; + case ObjectType.Event: group = StandardGlyphGroup.GlyphGroupEvent; break; + case ObjectType.Function: + case ObjectType.Method: + default: + group = StandardGlyphGroup.GlyphGroupMethod; + break; + } + return group; + } +#endif + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyTools/SolutionAdvisor.cs b/Tools/IronStudio/IronRubyTools/IronRubyTools/SolutionAdvisor.cs new file mode 100644 index 0000000000..f2a88dd71a --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyTools/SolutionAdvisor.cs @@ -0,0 +1,78 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio; + +namespace Microsoft.IronRubyTools { + class SolutionAdvisor : IVsSolutionEvents { + // private int _rubyProjectCount = 0; + + public int OnAfterCloseSolution(object pUnkReserved) { + return VSConstants.S_OK; + } + + public int OnAfterLoadProject(IVsHierarchy pStubHierarchy, IVsHierarchy pRealHierarchy) { + return VSConstants.S_OK; + } + + public int OnAfterOpenProject(IVsHierarchy pHierarchy, int fAdded) { + //var project = pHierarchy.GetProject().GetRubyProject(); + //if (project != null) { + // if (_rubyProjectCount++ == 0) { + // IronRubyToolsPackage.Instance.Analyzer.ImplicitProject = false; + // } + //} + return VSConstants.S_OK; + } + + public int OnAfterOpenSolution(object pUnkReserved, int fNewSolution) { + return VSConstants.S_OK; + } + + public int OnBeforeCloseProject(IVsHierarchy pHierarchy, int fRemoved) { + //var project = pHierarchy.GetProject().GetRubyProject(); + //if (project != null) { + // if (--_rubyProjectCount == 0) { + // IronRubyToolsPackage.Instance.Analyzer.ImplicitProject = true; + // } + //} + return VSConstants.S_OK; + } + + public int OnBeforeCloseSolution(object pUnkReserved) { + return VSConstants.S_OK; + } + + public int OnBeforeUnloadProject(IVsHierarchy pRealHierarchy, IVsHierarchy pStubHierarchy) { + return VSConstants.S_OK; + } + + public int OnQueryCloseProject(IVsHierarchy pHierarchy, int fRemoving, ref int pfCancel) { + return VSConstants.S_OK; + } + + public int OnQueryCloseSolution(object pUnkReserved, ref int pfCancel) { + return VSConstants.S_OK; + } + + public int OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int pfCancel) { + return VSConstants.S_OK; + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/IronRubyToolsPackage.cs b/Tools/IronStudio/IronRubyTools/IronRubyToolsPackage.cs new file mode 100644 index 0000000000..2895763e5c --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/IronRubyToolsPackage.cs @@ -0,0 +1,340 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using IronRuby.Runtime; +using Microsoft.IronRubyTools.Intellisense; +using Microsoft.IronRubyTools.Navigation; +using Microsoft.IronRubyTools.Options; +using Microsoft.IronRubyTools.Project; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Navigation; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.TextManager.Interop; +using Microsoft.VisualStudio.Utilities; +using System.Windows; +using Microsoft.IronStudio.Project; + +namespace Microsoft.IronRubyTools { + /// + /// This is the class that implements the package exposed by this assembly. + /// + /// The minimum requirement for a class to be considered a valid package for Visual Studio + /// is to implement the IVsPackage interface and register itself with the shell. + /// This package uses the helper classes defined inside the Managed Package Framework (MPF) + /// to do it: it derives from the Package class that provides the implementation of the + /// IVsPackage interface and uses the registration attributes defined in the framework to + /// register itself and its components with the shell. + /// + [PackageRegistration(UseManagedResourcesOnly = true)] // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is a package. + [InstalledProductRegistration("#110", "#112", "1.0", // This attribute is used to register the informations needed to show the this package in the Help/About dialog of Visual Studio. + IconResourceID = 400)] + [ProvideMenuResource(1000, 1)] // This attribute is needed to let the shell know that this package exposes some menus. + [ProvideAutoLoad(CommonConstants.UIContextNoSolution)] + [ProvideAutoLoad(CommonConstants.UIContextSolutionExists)] + [ProvideLanguageEditorOptionPage(typeof(RubyOptionsPage), RubyConstants.LanguageName, "Advanced", "", "113")] + [Guid(GuidList.guidIronRubyToolsPkgString)] // our packages GUID + [ProvideLanguageService(typeof(RubyLanguageInfo), RubyConstants.LanguageName, 106, RequestStockColors = true, DefaultToInsertSpaces = true, EnableCommenting = true)] + [ProvideLanguageExtension(typeof(RubyLanguageInfo), ".rb")] + [ProvideLanguageExtension(typeof(RubyLanguageInfo), ".ru")] + [ProvideLanguageExtension(typeof(RubyLanguageInfo), ".gemspec")] + public sealed class IronRubyToolsPackage : CommonPackage { + public static IronRubyToolsPackage Instance; + + internal readonly string IronRubyBinPath; + internal readonly string IronRubyExecutable; + internal readonly string IronRubyWindowsExecutable; + internal readonly string IronRubyToolsPath; + internal readonly string GemsBinPath; + + internal bool IronRubyInstalled { + get { return IronRubyBinPath != IronRubyToolsPath; } + } + + /// + /// Default constructor of the package. + /// Inside this method you can place any initialization code that does not require + /// any Visual Studio service because at this point the package object is created but + /// not sited yet inside Visual Studio environment. The place to do all the other + /// initialization is the Initialize method. + /// + public IronRubyToolsPackage() { + Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering constructor for: {0}", this.ToString())); + Instance = this; + + LoadAssemblies(); + InitializeRegistrySettings(); + IronRubyToolsPath = Path.GetDirectoryName(typeof(IronRubyToolsPackage).Assembly.Location); + IronRubyBinPath = GetIronRubyBinPath() ?? IronRubyToolsPath; + IronRubyExecutable = Path.Combine(IronRubyBinPath, RubyConstants.IronRubyExecutable); + IronRubyWindowsExecutable = Path.Combine(IronRubyBinPath, RubyConstants.IronRubyWindowsExecutable); + GemsBinPath = FindGemBinPath(IronRubyBinPath); + } + + private static string GetIronRubyBinPath() { + string result; + +#if DEBUG + result = Environment.GetEnvironmentVariable("DLR_ROOT"); + if (result != null) { + result = Path.Combine(result, @"Bin\Debug"); + if (IronRubyExistsIn(result)) { + return result; + } + } +#endif + result = Environment.GetEnvironmentVariable(RubyContext.BinDirEnvironmentVariable); + if (result != null && IronRubyExistsIn(result)) { + return result; + } + + var paths = Environment.GetEnvironmentVariable("PATH"); + if (paths != null) { + foreach (string dir in paths.Split(Path.PathSeparator)) { + try { + if (IronRubyExistsIn(dir)) { + return dir; + } + } catch { + // ignore + } + } + } + + return null; + } + + private static bool IronRubyExistsIn(string/*!*/ dir) { + return File.Exists(Path.Combine(dir, RubyConstants.IronRubyExecutable)); + } + + private static string FindGemBinPath(string/*!*/ ironRubyBinPath) { + string result; + result = Environment.GetEnvironmentVariable("GEM_PATH"); + if (Directory.Exists(result)) { + return result; + } + + try { + result = Path.Combine(RubyUtils.GetHomeDirectory(PlatformAdaptationLayer.Default), @"ironruby\1.9.1\bin"); + if (Directory.Exists(result)) { + return result; + } + } catch { + // ignore + } + + result = Path.Combine(ironRubyBinPath, @"Lib\ironruby\gems\1.9.1"); + if (Directory.Exists(result)) { + return result; + } + + result = Path.Combine(ironRubyBinPath, @"bin"); + if (Directory.Exists(result)) { + return result; + } + + return null; + } + + /// + /// VS seems to load extensions via Assembly.LoadFrom. When an assembly is being loaded via Assembly.Load the CLR fusion probes privatePath + /// set in App.config (devenv.exe.config) first and then tries the code base of the assembly that called Assembly.Load if it was itself loaded via LoadFrom. + /// In order to locate our assemblies correctly, the call to Assembly.Load must originate from an assembly in IronRubyTools installation folder. + /// + private static void LoadAssemblies() { + GC.KeepAlive(typeof(IronRuby.Builtins.HashOps)); // IronRuby.Libraries + } + + private bool _ironRubyInstallationRequirementShown = false; + + internal bool RequireIronRubyInstalled(bool allowCancel) { + if (!IronRubyInstalled && !_ironRubyInstallationRequirementShown) { + _ironRubyInstallationRequirementShown = true; + + return MessageBox.Show( + "IronRuby installation not found. Although basic scripts should work standard libraries and gems won't be available.\r\n" + + "Please download and install the latest release of IronRuby from http://ironruby.codeplex.com.", + "IronRuby not installed", + allowCancel ? MessageBoxButton.OKCancel : MessageBoxButton.OK, + MessageBoxImage.Warning, + MessageBoxResult.OK + ) == MessageBoxResult.OK; + } + + return true; + } + + private void InitializeRegistrySettings() { + using (var textEditor = VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_UserSettings, true).CreateSubKey("Text Editor")) { + using (var ironRubyKey = textEditor.CreateSubKey(RubyConstants.TextEditorSettingsRegistryKey)) { + object curValue; + curValue = ironRubyKey.GetValue("Insert Tabs"); + if (curValue == null) { + ironRubyKey.SetValue("Insert Tabs", 0); + } + + curValue = ironRubyKey.GetValue("Indent Size"); + if (curValue == null) { + ironRubyKey.SetValue("Indent Size", 2); + } + } + } + } + + internal static void NavigateTo(string view, Guid docViewGuidType, int line, int col) { + IVsTextManager textMgr = (IVsTextManager)Instance.GetService(typeof(SVsTextManager)); + var model = Instance.GetService(typeof(SComponentModel)) as IComponentModel; + var adapter = model.GetService(); + + IVsTextView viewAdapter; + IVsUIShellOpenDocument uiShellOpenDocument = Instance.GetService(typeof(SVsUIShellOpenDocument)) as IVsUIShellOpenDocument; + IVsUIHierarchy hierarchy; + uint itemid; + IVsWindowFrame pWindowFrame; + + VsShellUtilities.OpenDocument( + Instance, + view, + Guid.Empty, + out hierarchy, + out itemid, + out pWindowFrame, + out viewAdapter); + + ErrorHandler.ThrowOnFailure(pWindowFrame.Show()); + + // Set the cursor at the beginning of the declaration. + ErrorHandler.ThrowOnFailure(viewAdapter.SetCaretPos(line, col)); + // Make sure that the text is visible. + viewAdapter.CenterLines(line, 1); + /*TextSpan visibleSpan = new TextSpan(); + visibleSpan.iStartLine = line; + visibleSpan.iStartIndex = col; + visibleSpan.iEndLine = line; + visibleSpan.iEndIndex = col + 1;*/ + //ErrorHandler.ThrowOnFailure(viewAdapter.EnsureSpanVisible(visibleSpan)); + } + + public override ScriptEngine CreateScriptEngine() { + return RuntimeHost.RubyScriptEngine; + } + + public override Type GetLibraryManagerType() { + return typeof(IRubyLibraryManager); + } + + internal RubyOptionsPage OptionsPage { + get { + return (RubyOptionsPage)GetDialogPage(typeof(RubyOptionsPage)); + } + } + + private RubyAnalyzer _analyzer; + + internal RubyAnalyzer Analyzer { + get { + if (_analyzer == null) { + var model = GetService(typeof(SComponentModel)) as IComponentModel; + _analyzer = new RubyAnalyzer(model); + } + return _analyzer; + } + } + + public override LibraryManager CreateLibraryManager(CommonPackage package) { + return new RubyLibraryManager((IronRubyToolsPackage)package); + } + + public IVsSolution Solution { + get { + return GetService(typeof(SVsSolution)) as IVsSolution; + } + } + + public IRubyRuntimeHost RuntimeHost { + get { + return ComponentModel.GetService(); + } + } + + ///////////////////////////////////////////////////////////////////////////// + // Overriden Package Implementation + + /// + /// Initialization of the package; this method is called right after the package is sited, so this is the place + /// where you can put all the initilaization code that rely on services provided by VisualStudio. + /// + protected override void Initialize() { + Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "Entering Initialize() of: {0}", this.ToString())); + base.Initialize(); + + // Add our command handlers for menu (commands must exist in the .vsct file) + OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService; + if (null != mcs) { + foreach (var command in Commands.CommandTable.Commands) { + var beforeQueryStatus = command.BeforeQueryStatus; + CommandID toolwndCommandID = new CommandID(GuidList.guidIronRubyToolsCmdSet, command.CommandId); + if (beforeQueryStatus == null) { + MenuCommand menuToolWin = new MenuCommand(command.DoCommand, toolwndCommandID); + mcs.AddCommand(menuToolWin); + } else { + OleMenuCommand menuToolWin = new OleMenuCommand(command.DoCommand, toolwndCommandID); + menuToolWin.BeforeQueryStatus += beforeQueryStatus; + mcs.AddCommand(menuToolWin); + } + } + } + + // This is just to force the MEF creation of the DLR runtime hosting layer, including the content type and runtime. + if (RuntimeHost == null) { + throw new InvalidOperationException("Unable to obtain the DLR Runtime hosting component"); + } + + RuntimeHost.EnterOutliningModeOnOpen = OptionsPage.EnterOutliningModeOnOpen; + + // register our language service so that we can support features like the navigation bar + var langService = new RubyLanguageInfo(this); + ((IServiceContainer)this).AddService(langService.GetType(), langService, true); + + var solution = (IVsSolution)Package.GetGlobalService(typeof(SVsSolution)); + uint cookie; + ErrorHandler.ThrowOnFailure(solution.AdviseSolutionEvents(new SolutionAdvisor(), out cookie)); + } + + public EnvDTE.DTE DTE { + get { + return (EnvDTE.DTE)GetService(typeof(EnvDTE.DTE)); + } + } + + public IContentType ContentType { + get { + return RuntimeHost.ContentType; + } + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/Overview.txt b/Tools/IronStudio/IronRubyTools/Overview.txt new file mode 100644 index 0000000000..ea988e709d --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Overview.txt @@ -0,0 +1,39 @@ +Namespace Overview: + + Scripting\ + Things that logically be long in Microsoft.Scripting (e.g. pure DLR utils) + + VisualStudio\ + MPFProj implementation + + IronStudio\ + All common base infrastructure sharable by multiple languages + Includes misc helpers such as DlrUtils namespace, VsExtensions class + + Navigation\ + Contains everything related to object browser support and navigating around code + Project\ + Includes everything related to the project system + Repl\ + Common REPL implementation + + IronRubyTools\ + All IronRuby specific VS code: + Intellisense\ + Includes everything related to intellisense (collapses in Analysis sub directory) + Navigation\ + Includes everything related to code navigation features such as the object browser. + Project\ + Includes everything related to the project system + Commands\ + Includes implementation of IronRuby commands such as displaying the REPL window, + showing text in the REPL, etc... + Templates\ + Includes templates for items (single files) and projects. To add a new template you need + to: + create a corresponding .vstemplate file w/ associated content and put it into a ZIP file + add the ZIP file to the project + set the ZIP file to have a Build Action of type Content and set Include in VSIX true + Add the ZIP files directory to the .vsixmanifest file + + \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/PkgCmdID.cs b/Tools/IronStudio/IronRubyTools/PkgCmdID.cs new file mode 100644 index 0000000000..10a72e7517 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/PkgCmdID.cs @@ -0,0 +1,29 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +// PkgCmdID.cs +// MUST match PkgCmdID.h +using System; + +namespace Microsoft.IronRubyTools +{ + static class PkgCmdIDList + { + + public const uint cmdidReplWindow = 0x101; + public const uint cmdidExecuteFileInRepl = 0x102; + public const uint cmdidSendToRepl = 0x103; + + }; +} \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/Properties/AssemblyInfo.cs b/Tools/IronStudio/IronRubyTools/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..4dc8f0aed8 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Properties/AssemblyInfo.cs @@ -0,0 +1,34 @@ +using System; +using System.Reflection; +using System.Resources; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("IronRubyTools")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("IronRubyTools")] +[assembly: AssemblyCopyright("")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] +[assembly: ComVisible(false)] +[assembly: CLSCompliant(false)] +[assembly: NeutralResourcesLanguage("en-US")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Revision and Build Numbers +// by using the '*' as shown below: + +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] + diff --git a/Tools/IronStudio/IronRubyTools/Rackup.rb b/Tools/IronStudio/IronRubyTools/Rackup.rb new file mode 100644 index 0000000000..8141b6c3b4 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Rackup.rb @@ -0,0 +1,4 @@ +require 'rubygems' +version = '>= 0' +gem 'rack', version +load Gem.bin_path('rack', 'rackup', version) diff --git a/Tools/IronStudio/IronRubyTools/Resources.Designer.cs b/Tools/IronStudio/IronRubyTools/Resources.Designer.cs new file mode 100644 index 0000000000..b9ad7e7ba3 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Resources.Designer.cs @@ -0,0 +1,135 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30128.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.IronRubyTools { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Can not create tool window.. + /// + internal static string CanNotCreateWindow { + get { + return ResourceManager.GetString("CanNotCreateWindow", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to . + /// + internal static string ExecutingStartupScript { + get { + return ResourceManager.GetString("ExecutingStartupScript", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to . + /// + internal static string InitializingFromProject { + get { + return ResourceManager.GetString("InitializingFromProject", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to . + /// + internal static string MissingStartupScript { + get { + return ResourceManager.GetString("MissingStartupScript", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to References. + /// + internal static string ReferencesNodeName { + get { + return ResourceManager.GetString("ReferencesNodeName", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to . + /// + internal static string SettingWorkDir { + get { + return ResourceManager.GetString("SettingWorkDir", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to IronRuby Command Line. + /// + internal static string ToolWindowTitle { + get { + return ResourceManager.GetString("ToolWindowTitle", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to . + /// + internal static string UpdatingSearchPath { + get { + return ResourceManager.GetString("UpdatingSearchPath", resourceCulture); + } + } + } +} diff --git a/Tools/IronStudio/IronRubyTools/Resources.resx b/Tools/IronStudio/IronRubyTools/Resources.resx new file mode 100644 index 0000000000..45015047f1 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Resources.resx @@ -0,0 +1,144 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Can not create tool window. + + + + + + + + + + + + References + + + + + + IronRuby Command Line + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/Resources/Images_24bit.bmp b/Tools/IronStudio/IronRubyTools/Resources/Images_24bit.bmp new file mode 100644 index 0000000000..585f941806 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Resources/Images_24bit.bmp differ diff --git a/Tools/IronStudio/IronRubyTools/Resources/Images_32bit.bmp b/Tools/IronStudio/IronRubyTools/Resources/Images_32bit.bmp new file mode 100644 index 0000000000..fc1174670d Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Resources/Images_32bit.bmp differ diff --git a/Tools/IronStudio/IronRubyTools/Resources/Package.ico b/Tools/IronStudio/IronRubyTools/Resources/Package.ico new file mode 100644 index 0000000000..502c54400e Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Resources/Package.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Resources/RubyPackageIcons.bmp b/Tools/IronStudio/IronRubyTools/Resources/RubyPackageIcons.bmp new file mode 100644 index 0000000000..0d79d9c2e1 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Resources/RubyPackageIcons.bmp differ diff --git a/Tools/IronStudio/IronRubyTools/Resources/completionset.bmp b/Tools/IronStudio/IronRubyTools/Resources/completionset.bmp new file mode 100644 index 0000000000..d4ea8c21a2 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Resources/completionset.bmp differ diff --git a/Tools/IronStudio/IronRubyTools/RubyConstants.cs b/Tools/IronStudio/IronRubyTools/RubyConstants.cs new file mode 100644 index 0000000000..72f76252ea --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/RubyConstants.cs @@ -0,0 +1,69 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.IronRubyTools { + + public static class RubyConstants { + public const string FileExtension = ".rb"; + public const string LanguageName = "IronRuby"; + public const string IronRubyExecutable = "ir.exe"; + public const string IronRubyWindowsExecutable = "ir.exe"; // TODO: irw.exe + public const string ProjectImageList = "Microsoft.RubyImageList.bmp"; + + public static class ProjectProperties { + public const string DisableDebugging = "DisableDebugging"; + public const string RubyApplicationType = "RubyApplicationType"; + public const string RubyApplicationType_WebApp = "WebApp"; + public const string DefaultPort = "DefaultPort"; + public const string Launcher = "Launcher"; + public const string Launcher_Rack = "Rack"; + public const string Launcher_Spec = "Spec"; + } + + public const string TextEditorSettingsRegistryKey = LanguageName; + public const string SnippetsIndex = @"IronRubyCodeSnippets\SnippetsIndex.xml"; + public const string SnippetsPath = @"IronRubyCodeSnippets\Snippets\"; + + public const string LanguageServiceGuid = "dcf49fd2-e5bf-436d-b9f3-caf4f9029d02"; + public const string LibraryManagerGuid = "06fa4cb1-f476-4f0e-978d-fd1f8263f346"; + public const string LibraryManagerServiceGuid = "8464e8f5-80a5-45ae-839f-135d5d781264"; + public static readonly Guid RubyConsoleCmdSetGuid = new Guid("7fdd285b-0e83-42ab-b17e-59602eabbc3f"); + + // Output window panes: + public static readonly Guid ProjectOutputPaneGuid = new Guid("FAD82010-4B27-4B8D-8C0E-8F427035E53B"); + + // Do not change below info without re-requesting PLK: + public const string PLKProductVersion = "2.0"; + public const string PLKProductName = "IronRuby Language Service"; + public const string LanguageServicePackageGuid = "5e71f8f4-ae7c-4f87-9898-a646b24b9c17"; //matches PLK + public const string ProjectSystemPackageGuid = "b76642b4-17e6-431e-9df4-09979a096ea3"; + // End of Do not change ... + + public const string ProjectFactoryGuid = "b373b46a-b089-44d7-96ca-cd150d098bc8"; + public const string EditorFactoryGuid = "7ae31113-65bd-4b13-8ea4-c8bd4387cc02"; + public const string ProjectNodeGuid = "e8f249c4-d140-4f73-ae92-ba550cc107fb"; + public const string GeneralPropertyPageGuid = "3c8e94a6-40b3-4eb5-803e-4e0994b11222"; + + + public static readonly Guid ProjectCmdSetGuid = new Guid("2f37fdf1-7008-4d22-a856-853c74cb3d95"); + + + //IDs of the icons for product registration (see Resources.resx) + public const int IconIfForSplashScreen = 300; + public const int IconIdForAboutBox = 400; + + } +} diff --git a/Tools/IronStudio/IronRubyTools/RubyImageList.bmp b/Tools/IronStudio/IronRubyTools/RubyImageList.bmp new file mode 100644 index 0000000000..29e220c140 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/RubyImageList.bmp differ diff --git a/Tools/IronStudio/IronRubyTools/RubyProject.ico b/Tools/IronStudio/IronRubyTools/RubyProject.ico new file mode 100644 index 0000000000..502c54400e Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/RubyProject.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Spec.rb b/Tools/IronStudio/IronRubyTools/Spec.rb new file mode 100644 index 0000000000..a2dc549685 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Spec.rb @@ -0,0 +1,4 @@ +require 'rubygems' +version = '>= 0' +gem 'rspec', version +load Gem.bin_path('rspec', 'spec', version) diff --git a/Tools/IronStudio/IronRubyTools/Templates/Files/EmptyRbFile/EmptyRbFile.zip b/Tools/IronStudio/IronRubyTools/Templates/Files/EmptyRbFile/EmptyRbFile.zip new file mode 100644 index 0000000000..d8f8b7fae3 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Templates/Files/EmptyRbFile/EmptyRbFile.zip differ diff --git a/Tools/IronStudio/IronRubyTools/Templates/Files/EmptyRbFile/__TemplateIcon.ico b/Tools/IronStudio/IronRubyTools/Templates/Files/EmptyRbFile/__TemplateIcon.ico new file mode 100644 index 0000000000..5933477959 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Templates/Files/EmptyRbFile/__TemplateIcon.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Templates/Files/EmptyRbFile/empty.rb b/Tools/IronStudio/IronRubyTools/Templates/Files/EmptyRbFile/empty.rb new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Tools/IronStudio/IronRubyTools/Templates/Files/EmptyRbFile/empty.vstemplate b/Tools/IronStudio/IronRubyTools/Templates/Files/EmptyRbFile/empty.vstemplate new file mode 100644 index 0000000000..740059ab50 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Files/EmptyRbFile/empty.vstemplate @@ -0,0 +1,13 @@ + + + Empty Ruby File + An empty .rb file + __TemplateIcon.ico + IronRuby + empty.rb + + + + empty.rb + + diff --git a/Tools/IronStudio/IronRubyTools/Templates/Files/RSpecFile/RSpecFile.rb b/Tools/IronStudio/IronRubyTools/Templates/Files/RSpecFile/RSpecFile.rb new file mode 100644 index 0000000000..5a8f92d604 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Files/RSpecFile/RSpecFile.rb @@ -0,0 +1,8 @@ +require 'rubygems' +require 'spec' + +describe 'New specs' do + it 'should fail' do + true.should == false + end +end \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/Templates/Files/RSpecFile/RSpecFile.vstemplate b/Tools/IronStudio/IronRubyTools/Templates/Files/RSpecFile/RSpecFile.vstemplate new file mode 100644 index 0000000000..0fe72f9d6e --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Files/RSpecFile/RSpecFile.vstemplate @@ -0,0 +1,13 @@ + + + RSpec test file + RSpec test file + __TemplateIcon.ico + IronRuby + my_spec.rb + + + + RSpecFile.rb + + diff --git a/Tools/IronStudio/IronRubyTools/Templates/Files/RSpecFile/RSpecFile.zip b/Tools/IronStudio/IronRubyTools/Templates/Files/RSpecFile/RSpecFile.zip new file mode 100644 index 0000000000..4ca9c3de76 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Templates/Files/RSpecFile/RSpecFile.zip differ diff --git a/Tools/IronStudio/IronRubyTools/Templates/Files/RSpecFile/__TemplateIcon.ico b/Tools/IronStudio/IronRubyTools/Templates/Files/RSpecFile/__TemplateIcon.ico new file mode 100644 index 0000000000..5933477959 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Templates/Files/RSpecFile/__TemplateIcon.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Templates/Files/Rakefile/Rakefile.zip b/Tools/IronStudio/IronRubyTools/Templates/Files/Rakefile/Rakefile.zip new file mode 100644 index 0000000000..b3204c8026 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Templates/Files/Rakefile/Rakefile.zip differ diff --git a/Tools/IronStudio/IronRubyTools/Templates/Files/Rakefile/__TemplateIcon.ico b/Tools/IronStudio/IronRubyTools/Templates/Files/Rakefile/__TemplateIcon.ico new file mode 100644 index 0000000000..9ad591eaa0 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Templates/Files/Rakefile/__TemplateIcon.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Templates/Files/Rakefile/rakefile b/Tools/IronStudio/IronRubyTools/Templates/Files/Rakefile/rakefile new file mode 100644 index 0000000000..c5da5d0778 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Files/Rakefile/rakefile @@ -0,0 +1,4 @@ +desc "Say Hello" +task :say_hello do + puts 'hello' +end \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/Templates/Files/Rakefile/rakefile.vstemplate b/Tools/IronStudio/IronRubyTools/Templates/Files/Rakefile/rakefile.vstemplate new file mode 100644 index 0000000000..dcc3019818 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Files/Rakefile/rakefile.vstemplate @@ -0,0 +1,13 @@ + + + Empty Ruby File + Rakefile + __TemplateIcon.ico + IronRuby + rakefile + + + + rakefile + + diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/ConsoleAppProject/ConsoleApp.zip b/Tools/IronStudio/IronRubyTools/Templates/Projects/ConsoleAppProject/ConsoleApp.zip new file mode 100644 index 0000000000..21f5694723 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Templates/Projects/ConsoleAppProject/ConsoleApp.zip differ diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/ConsoleAppProject/IRConsoleApp.vstemplate b/Tools/IronStudio/IronRubyTools/Templates/Projects/ConsoleAppProject/IRConsoleApp.vstemplate new file mode 100644 index 0000000000..a8d275721c --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/ConsoleAppProject/IRConsoleApp.vstemplate @@ -0,0 +1,18 @@ + + + Console Application + A project for creating a command-line application + __TemplateIcon.ico + IronRuby + 50 + 1 + true + ConsoleApplication + true + + + + Program.rb + + + diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/ConsoleAppProject/IronRubyApp.rbproj b/Tools/IronStudio/IronRubyTools/Templates/Projects/ConsoleAppProject/IronRubyApp.rbproj new file mode 100644 index 0000000000..df90ec2548 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/ConsoleAppProject/IronRubyApp.rbproj @@ -0,0 +1,22 @@ + + + Debug + 2.0 + $guid1$ + . + Program.rb + + . + + + true + false + + + true + false + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/ConsoleAppProject/Program.rb b/Tools/IronStudio/IronRubyTools/Templates/Projects/ConsoleAppProject/Program.rb new file mode 100644 index 0000000000..c8528fbfe1 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/ConsoleAppProject/Program.rb @@ -0,0 +1 @@ +puts 'Hello World' diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/ConsoleAppProject/__TemplateIcon.ico b/Tools/IronStudio/IronRubyTools/Templates/Projects/ConsoleAppProject/__TemplateIcon.ico new file mode 100644 index 0000000000..ae36f5cbe5 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Templates/Projects/ConsoleAppProject/__TemplateIcon.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/CHANGELOG b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/CHANGELOG new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/Gem.rbproj b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/Gem.rbproj new file mode 100644 index 0000000000..602327e5f0 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/Gem.rbproj @@ -0,0 +1,29 @@ + + + Debug + 2.0 + $guid1$ + . + + . + true + RSpec + Spec + + + true + false + + + true + false + + + + + + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/Gem.vstemplate b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/Gem.vstemplate new file mode 100644 index 0000000000..0d952ea037 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/Gem.vstemplate @@ -0,0 +1,24 @@ + + + Ruby Gem + Ruby Gem + __TemplateIcon.ico + IronRuby + 50 + 1 + false + my_gem + true + + + + __Gems.rb + CHANGELOG + LICENSE + README + test\program_spec.rb + lib\program.rb + program.gemspec + + + diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/Gem.zip b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/Gem.zip new file mode 100644 index 0000000000..00a5d066be Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/Gem.zip differ diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/LICENSE b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/LICENSE new file mode 100644 index 0000000000..e69de29bb2 diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/README b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/README new file mode 100644 index 0000000000..100b93820a --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/README @@ -0,0 +1 @@ +README \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/__Gems.rb b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/__Gems.rb new file mode 100644 index 0000000000..189d449fac --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/__Gems.rb @@ -0,0 +1,63 @@ +puts "Creating Gem $safeprojectname$ in #{Dir.pwd} ..." + +require 'rubygems' +require 'rubygems/commands/install_command' + +class GemCmd < Gem::Commands::InstallCommand + def initialize + super + options[:user_install] = true + options[:cache_dir] = Gem.user_dir + options[:generate_rdoc] = false + options[:generate_ri] = false + end + + def install *gems + puts "Installing missing gems: #{gems.map { |name, version| if version.nil? then name else "#{name} (#{version})" end }.join(", ")}" + gem_home = nil + all_installed_gems = [] + + gems.each do |gem_name, gem_version| + begin + + inst = Gem::DependencyInstaller.new options + inst.install gem_name, gem_version || options[:version] + + inst.installed_gems.each do |spec| + gem_home ||= File.expand_path(File.join(File.dirname(spec.loaded_from), '..')) + all_installed_gems << spec + puts "Successfully installed #{spec.full_name}" + end + + rescue Gem::InstallError => e + STDERR.puts "Error installing #{gem_name}:\n\t#{e.message}" + rescue Gem::GemNotFoundException => e + STDERR.puts e.message + rescue Exception => e + STDERR.puts e.message + end + end + + gems = all_installed_gems.length == 1 ? 'gem' : 'gems' + puts "#{all_installed_gems.length} #{gems} installed" + + gem_home + end +end + +rspec_version = "1.3.0" +gems_installed = false + +begin + gem 'rspec', rspec_version +rescue Gem::LoadError + raise if gems_installed + + # install gems: + gem_home = GemCmd.new.install(["rspec", rspec_version]) + exit 1 if gem_home.nil? + ENV['GEM_PATH'] = gem_home if gem_home != ENV['GEM_PATH'] + + gems_installed = true + retry +end diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/__TemplateIcon.ico b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/__TemplateIcon.ico new file mode 100644 index 0000000000..cb20eefc3e Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/__TemplateIcon.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/lib/program.rb b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/lib/program.rb new file mode 100644 index 0000000000..ef97526c7b --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/lib/program.rb @@ -0,0 +1,5 @@ +class Program + def hello_world + return 42 + end +end \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/program.gemspec b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/program.gemspec new file mode 100644 index 0000000000..613f6ca4cb --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/program.gemspec @@ -0,0 +1,24 @@ +Gem::Specification.new do |s| + s.platform = "universal-dotnet" + s.name = '$safeprojectname$' + s.version = '0.1' + s.summary = "Your Gem's summary" + s.description = "Your Gem's description" + s.required_ruby_version = '>= 1.9.1' + + s.author = 'Your Name' + s.email = 'your@email' + s.homepage = 'http://yourhomepage' + + s.rubyforge_project = '$safeprojectname$' + + s.files = ['CHANGELOG', 'README', 'LICENSE'] + Dir['lib/**/*'] + s.require_path = 'lib' + s.requirements << 'none' + + s.has_rdoc = true + + # Add other gem dependencies + # s.add_dependency('name', '= 1.0.0') +end + diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/test/program_spec.rb b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/test/program_spec.rb new file mode 100644 index 0000000000..177eedd18e --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/GemProject/test/program_spec.rb @@ -0,0 +1,11 @@ +require 'rubygems' +require 'spec' +$: << File.dirname(__FILE__) + '/../lib' + +require '$safeprojectname$' + +describe 'Program' do + it 'should work' do + Program.new.hello_world.should == 42 + end +end \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/Gemfile b/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/Gemfile new file mode 100644 index 0000000000..ff0f871644 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/Gemfile @@ -0,0 +1,11 @@ +source 'http://rubygems.org' + +gem 'rails', '3.0.1' +gem 'activerecord-sqlserver-adapter', :require => 'activerecord-sqlserver-adapter' + +# Bundle gems for the local environment. Make sure to +# put test-only gems in this group so their generators +# and rake tasks are available in development mode: +# group :development, :test do +# gem 'webrat' +# end diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/RubyOnRailsApp.rbproj b/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/RubyOnRailsApp.rbproj new file mode 100644 index 0000000000..38feac20df --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/RubyOnRailsApp.rbproj @@ -0,0 +1,23 @@ + + + Debug + 2.0 + $guid1$ + . + script\rails + server + + . + true + WebApp + 3000 + + + true + false + + + true + false + + \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/RubyOnRailsApp.vstemplate b/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/RubyOnRailsApp.vstemplate new file mode 100644 index 0000000000..a99a16775a --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/RubyOnRailsApp.vstemplate @@ -0,0 +1,20 @@ + + + Ruby on Rails Application + Ruby on Rails Application + __TemplateIcon.ico + IronRuby + 50 + 1 + false + WebApp + true + + + + __TemplateScript.rb + config\database.yml + Gemfile + + + diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/RubyOnRailsApp.zip b/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/RubyOnRailsApp.zip new file mode 100644 index 0000000000..0bb1c2da57 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/RubyOnRailsApp.zip differ diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/__TemplateIcon.ico b/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/__TemplateIcon.ico new file mode 100644 index 0000000000..46098edc13 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/__TemplateIcon.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/__TemplateScript.rb b/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/__TemplateScript.rb new file mode 100644 index 0000000000..384df76b27 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/__TemplateScript.rb @@ -0,0 +1,95 @@ +app_name = "$safeprojectname$" + +puts "Creating Rails app #{app_name} in #{Dir.pwd} ..." + +require 'rubygems' +require 'rubygems/commands/install_command' + +class GemCmd < Gem::Commands::InstallCommand + def initialize + super + options[:user_install] = true + options[:cache_dir] = Gem.user_dir + options[:generate_rdoc] = false + options[:generate_ri] = false + end + + def install *gems + puts "Installing missing gems: #{gems.map { |name, version| if version.nil? then name else "#{name} (#{version})" end }.join(", ")}" + gem_home = nil + all_installed_gems = [] + + gems.each do |gem_name, gem_version| + begin + + inst = Gem::DependencyInstaller.new options + inst.install gem_name, gem_version || options[:version] + + inst.installed_gems.each do |spec| + gem_home ||= File.expand_path(File.join(File.dirname(spec.loaded_from), '..')) + all_installed_gems << spec + puts "Successfully installed #{spec.full_name}" + end + + rescue Gem::InstallError => e + STDERR.puts "Error installing #{gem_name}:\n\t#{e.message}" + rescue Gem::GemNotFoundException => e + STDERR.puts e.message + rescue Exception => e + STDERR.puts e.message + end + end + + gems = all_installed_gems.length == 1 ? 'gem' : 'gems' + puts "#{all_installed_gems.length} #{gems} installed" + + gem_home + end +end + +def create_database server, db + puts "Creating database #{db} on #{server} ..." + + require 'System.Data' + sql_client = System::Data::SqlClient + + connection = sql_client::SqlConnection.new("Data Source=#{server};Integrated Security=True") + command = sql_client::SqlCommand.new("CREATE DATABASE [#{db}]", connection) + connection.open + command.execute_non_query + + puts 'Database created.' +rescue Exception => e + puts "Error: #{e.message}" +ensure + connection.close unless connection.nil? +end + +# generate application files: + +rails_version = "3.0.1" +sqlserver_adapter_version = "3.0.2" +gems_installed = false + +begin + gem 'rails', rails_version +rescue Gem::LoadError + raise if gems_installed + + # install gems: + gem_home = GemCmd.new.install(["rails", rails_version], ["activerecord-sqlserver-adapter", sqlserver_adapter_version]) + exit 1 if gem_home.nil? + ENV['GEM_PATH'] = gem_home if gem_home != ENV['GEM_PATH'] + + gems_installed = true + retry +end + +# create rails app: +ARGV.clear +ARGV << "new" +ARGV << app_name +require "rails/cli" + +# create database: +create_database "$machinename$\\SQLEXPRESS", "$safeprojectname$" diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/config/database.yml b/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/config/database.yml new file mode 100644 index 0000000000..0a33274e3c --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/RubyOnRailsAppProject/config/database.yml @@ -0,0 +1,23 @@ +development: + mode: ADONET + adapter: sqlserver + host: $machinename$\SQLEXPRESS + database: $safeprojectname$ + integrated_security: true + +# Warning: The database defined as "test" will be erased and +# re-generated from your development database when you run "rake". +# Do not set this db to the same as development or production. +test: + mode: ADONET + adapter: sqlserver + host: $machinename$\SQLEXPRESS + database: $safeprojectname$ + integrated_security: true + +production: + mode: ADONET + adapter: sqlserver + host: $machinename$\SQLEXPRESS + database: $safeprojectname$ + integrated_security: true \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/Program.html b/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/Program.html new file mode 100644 index 0000000000..e4dbb2dc65 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/Program.html @@ -0,0 +1,15 @@ + + + + + + + $projectname$ + + + + + + diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/Program.rb b/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/Program.rb new file mode 100644 index 0000000000..39859e0678 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/Program.rb @@ -0,0 +1,3 @@ +document.say_hello.onclick do |s, e| + window.alert("Hello, world!") +end diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/SilverlightProject.rbproj b/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/SilverlightProject.rbproj new file mode 100644 index 0000000000..ed916302d5 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/SilverlightProject.rbproj @@ -0,0 +1,23 @@ + + + Debug + 2.0 + $guid1$ + . + $safeprojectname$.html + + . + + + true + false + + + true + false + + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/SilverlightProject.vstemplate b/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/SilverlightProject.vstemplate new file mode 100644 index 0000000000..c088ed8c39 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/SilverlightProject.vstemplate @@ -0,0 +1,17 @@ + + + IronRuby Silverlight Web Page + A project for creating a web page scripted on the client side with IronRuby via Silverlight + __TemplateIcon.ico + IronRuby + true + SilverlightPage + true + + + + Program.html + Program.rb + + + diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/SilverlightProject.zip b/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/SilverlightProject.zip new file mode 100644 index 0000000000..821b927813 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/SilverlightProject.zip differ diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/__TemplateIcon.ico b/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/__TemplateIcon.ico new file mode 100644 index 0000000000..1a7ed68953 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Templates/Projects/SilverlightProject/__TemplateIcon.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/Sinatra.rbproj b/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/Sinatra.rbproj new file mode 100644 index 0000000000..f7078bc88b --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/Sinatra.rbproj @@ -0,0 +1,27 @@ + + + Debug + 2.0 + $guid1$ + . + config.ru + + . + true + WebApp + 9292 + Rack + + + true + false + + + true + false + + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/Sinatra.vstemplate b/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/Sinatra.vstemplate new file mode 100644 index 0000000000..d07e48991f --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/Sinatra.vstemplate @@ -0,0 +1,20 @@ + + + Sinatra Web Application + Sinatra Web Application + __TemplateIcon.ico + IronRuby + 50 + 1 + false + WebApp + true + + + + __Gems.rb + config.ru + app.rb + + + diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/Sinatra.zip b/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/Sinatra.zip new file mode 100644 index 0000000000..e7797d44cf Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/Sinatra.zip differ diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/__Gems.rb b/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/__Gems.rb new file mode 100644 index 0000000000..9c59189c63 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/__Gems.rb @@ -0,0 +1,63 @@ +puts "Creating Sinatra app $safeprojectname$ in #{Dir.pwd} ..." + +require 'rubygems' +require 'rubygems/commands/install_command' + +class GemCmd < Gem::Commands::InstallCommand + def initialize + super + options[:user_install] = true + options[:cache_dir] = Gem.user_dir + options[:generate_rdoc] = false + options[:generate_ri] = false + end + + def install *gems + puts "Installing missing gems: #{gems.map { |name, version| if version.nil? then name else "#{name} (#{version})" end }.join(", ")}" + gem_home = nil + all_installed_gems = [] + + gems.each do |gem_name, gem_version| + begin + + inst = Gem::DependencyInstaller.new options + inst.install gem_name, gem_version || options[:version] + + inst.installed_gems.each do |spec| + gem_home ||= File.expand_path(File.join(File.dirname(spec.loaded_from), '..')) + all_installed_gems << spec + puts "Successfully installed #{spec.full_name}" + end + + rescue Gem::InstallError => e + STDERR.puts "Error installing #{gem_name}:\n\t#{e.message}" + rescue Gem::GemNotFoundException => e + STDERR.puts e.message + rescue Exception => e + STDERR.puts e.message + end + end + + gems = all_installed_gems.length == 1 ? 'gem' : 'gems' + puts "#{all_installed_gems.length} #{gems} installed" + + gem_home + end +end + +sinatra_version = "1.0" +gems_installed = false + +begin + gem 'sinatra', sinatra_version +rescue Gem::LoadError + raise if gems_installed + + # install gems: + gem_home = GemCmd.new.install(["sinatra", sinatra_version]) + exit 1 if gem_home.nil? + ENV['GEM_PATH'] = gem_home if gem_home != ENV['GEM_PATH'] + + gems_installed = true + retry +end diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/__TemplateIcon.ico b/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/__TemplateIcon.ico new file mode 100644 index 0000000000..46098edc13 Binary files /dev/null and b/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/__TemplateIcon.ico differ diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/app.rb b/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/app.rb new file mode 100644 index 0000000000..c31f163bec --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/app.rb @@ -0,0 +1,7 @@ +require 'rubygems' +require 'sinatra' + +get '/' do + @msg = 'Hello, World' + erb '<%= @msg %>' +end \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/config.ru b/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/config.ru new file mode 100644 index 0000000000..beb548b5d1 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Templates/Projects/SinatraProject/config.ru @@ -0,0 +1,6 @@ +require 'rubygems' +require 'sinatra' + +require '$safeprojectname$' + +run Sinatra::Application diff --git a/Tools/IronStudio/IronRubyTools/VSPackage.resx b/Tools/IronStudio/IronRubyTools/VSPackage.resx new file mode 100644 index 0000000000..7f8eb9636e --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/VSPackage.resx @@ -0,0 +1,154 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + IronRuby Tools for Visual Studio + + + IronRuby Tools for Visual Studio provides intellisense, project support, project and item templates, as well as a REPL window for IronRuby development. + + + + Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Resources\Images_24bit.bmp;System.Drawing.Bitmap, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Text Editor! + + + REPL Window + + + IronRuby + + + Console Application + + + WinForms Application + + + Wpf Application + + + IronRuby Silverlight Page + + \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/Vsix/IronRuby Tools for VS binary build FINAL 3Feb2010.rtf b/Tools/IronStudio/IronRubyTools/Vsix/IronRuby Tools for VS binary build FINAL 3Feb2010.rtf new file mode 100644 index 0000000000..5fe310bd12 --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/Vsix/IronRuby Tools for VS binary build FINAL 3Feb2010.rtf @@ -0,0 +1,702 @@ +{\rtf1\adeflang1025\ansi\ansicpg1252\uc1\adeff38\deff0\stshfdbch11\stshfloch0\stshfhich0\stshfbi0\deflang1033\deflangfe1033\themelang1033\themelangfe0\themelangcs0{\fonttbl{\f0\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};} +{\f2\fbidi \fmodern\fcharset0\fprq1{\*\panose 02070309020205020404}Courier New{\*\falt Arial};}{\f3\fbidi \froman\fcharset2\fprq2{\*\panose 05050102010706020507}Symbol{\*\falt Bookshelf Symbol 3};} +{\f10\fbidi \fnil\fcharset2\fprq2{\*\panose 05000000000000000000}Wingdings{\*\falt Symbol};}{\f11\fbidi \fmodern\fcharset128\fprq1{\*\panose 02020609040205080304}MS Mincho{\*\falt ?l?r ??\'81\'66c};} +{\f13\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}SimSun{\*\falt ??????\'a1\'a7????};}{\f13\fbidi \fnil\fcharset134\fprq2{\*\panose 02010600030101010101}SimSun{\*\falt ??????\'a1\'a7????};} +{\f38\fbidi \fswiss\fcharset0\fprq2{\*\panose 020b0604030504040204}Tahoma{\*\falt ?l?r ??u!??I};}{\f39\fbidi \fswiss\fcharset0\fprq2{\*\panose 00000000000000000000}Trebuchet MS{\*\falt Univers};} +{\f40\fbidi \fmodern\fcharset128\fprq1{\*\panose 00000000000000000000}@MS Mincho{\*\falt @MS Gothic};}{\f41\fbidi \fnil\fcharset134\fprq2{\*\panose 00000000000000000000}@SimSun;} +{\flomajor\f31500\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};}{\fdbmajor\f31501\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};} +{\fhimajor\f31502\fbidi \froman\fcharset0\fprq2{\*\panose 02040503050406030204}Cambria;}{\fbimajor\f31503\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};} +{\flominor\f31504\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};}{\fdbminor\f31505\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};} +{\fhiminor\f31506\fbidi \fswiss\fcharset0\fprq2{\*\panose 020f0502020204030204}Calibri;}{\fbiminor\f31507\fbidi \froman\fcharset0\fprq2{\*\panose 02020603050405020304}Times New Roman{\*\falt Times};} +{\f44\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};}{\f45\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};}{\f47\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};} +{\f48\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};}{\f49\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};}{\f50\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};} +{\f51\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};}{\f52\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}{\f64\fbidi \fmodern\fcharset238\fprq1 Courier New CE{\*\falt Arial};} +{\f65\fbidi \fmodern\fcharset204\fprq1 Courier New Cyr{\*\falt Arial};}{\f67\fbidi \fmodern\fcharset161\fprq1 Courier New Greek{\*\falt Arial};}{\f68\fbidi \fmodern\fcharset162\fprq1 Courier New Tur{\*\falt Arial};} +{\f69\fbidi \fmodern\fcharset177\fprq1 Courier New (Hebrew){\*\falt Arial};}{\f70\fbidi \fmodern\fcharset178\fprq1 Courier New (Arabic){\*\falt Arial};}{\f71\fbidi \fmodern\fcharset186\fprq1 Courier New Baltic{\*\falt Arial};} +{\f72\fbidi \fmodern\fcharset163\fprq1 Courier New (Vietnamese){\*\falt Arial};}{\f156\fbidi \fmodern\fcharset0\fprq1 MS Mincho Western{\*\falt ?l?r ??\'81\'66c};}{\f154\fbidi \fmodern\fcharset238\fprq1 MS Mincho CE{\*\falt ?l?r ??\'81\'66c};} +{\f155\fbidi \fmodern\fcharset204\fprq1 MS Mincho Cyr{\*\falt ?l?r ??\'81\'66c};}{\f157\fbidi \fmodern\fcharset161\fprq1 MS Mincho Greek{\*\falt ?l?r ??\'81\'66c};}{\f158\fbidi \fmodern\fcharset162\fprq1 MS Mincho Tur{\*\falt ?l?r ??\'81\'66c};} +{\f161\fbidi \fmodern\fcharset186\fprq1 MS Mincho Baltic{\*\falt ?l?r ??\'81\'66c};}{\f176\fbidi \fnil\fcharset0\fprq2 SimSun Western{\*\falt ??????\'a1\'a7????};}{\f176\fbidi \fnil\fcharset0\fprq2 SimSun Western{\*\falt ??????\'a1\'a7????};} +{\f424\fbidi \fswiss\fcharset238\fprq2 Tahoma CE{\*\falt ?l?r ??u!??I};}{\f425\fbidi \fswiss\fcharset204\fprq2 Tahoma Cyr{\*\falt ?l?r ??u!??I};}{\f427\fbidi \fswiss\fcharset161\fprq2 Tahoma Greek{\*\falt ?l?r ??u!??I};} +{\f428\fbidi \fswiss\fcharset162\fprq2 Tahoma Tur{\*\falt ?l?r ??u!??I};}{\f429\fbidi \fswiss\fcharset177\fprq2 Tahoma (Hebrew){\*\falt ?l?r ??u!??I};}{\f430\fbidi \fswiss\fcharset178\fprq2 Tahoma (Arabic){\*\falt ?l?r ??u!??I};} +{\f431\fbidi \fswiss\fcharset186\fprq2 Tahoma Baltic{\*\falt ?l?r ??u!??I};}{\f432\fbidi \fswiss\fcharset163\fprq2 Tahoma (Vietnamese){\*\falt ?l?r ??u!??I};}{\f433\fbidi \fswiss\fcharset222\fprq2 Tahoma (Thai){\*\falt ?l?r ??u!??I};} +{\f434\fbidi \fswiss\fcharset238\fprq2 Trebuchet MS CE{\*\falt Univers};}{\f435\fbidi \fswiss\fcharset204\fprq2 Trebuchet MS Cyr{\*\falt Univers};}{\f437\fbidi \fswiss\fcharset161\fprq2 Trebuchet MS Greek{\*\falt Univers};} +{\f438\fbidi \fswiss\fcharset162\fprq2 Trebuchet MS Tur{\*\falt Univers};}{\f441\fbidi \fswiss\fcharset186\fprq2 Trebuchet MS Baltic{\*\falt Univers};}{\f446\fbidi \fmodern\fcharset0\fprq1 @MS Mincho Western{\*\falt @MS Gothic};} +{\f444\fbidi \fmodern\fcharset238\fprq1 @MS Mincho CE{\*\falt @MS Gothic};}{\f445\fbidi \fmodern\fcharset204\fprq1 @MS Mincho Cyr{\*\falt @MS Gothic};}{\f447\fbidi \fmodern\fcharset161\fprq1 @MS Mincho Greek{\*\falt @MS Gothic};} +{\f448\fbidi \fmodern\fcharset162\fprq1 @MS Mincho Tur{\*\falt @MS Gothic};}{\f451\fbidi \fmodern\fcharset186\fprq1 @MS Mincho Baltic{\*\falt @MS Gothic};}{\f456\fbidi \fnil\fcharset0\fprq2 @SimSun Western;} +{\flomajor\f31508\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};}{\flomajor\f31509\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};} +{\flomajor\f31511\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};}{\flomajor\f31512\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};} +{\flomajor\f31513\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};}{\flomajor\f31514\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};} +{\flomajor\f31515\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};}{\flomajor\f31516\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};} +{\fdbmajor\f31518\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};}{\fdbmajor\f31519\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};} +{\fdbmajor\f31521\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};}{\fdbmajor\f31522\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};} +{\fdbmajor\f31523\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};}{\fdbmajor\f31524\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};} +{\fdbmajor\f31525\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};}{\fdbmajor\f31526\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}{\fhimajor\f31528\fbidi \froman\fcharset238\fprq2 Cambria CE;} +{\fhimajor\f31529\fbidi \froman\fcharset204\fprq2 Cambria Cyr;}{\fhimajor\f31531\fbidi \froman\fcharset161\fprq2 Cambria Greek;}{\fhimajor\f31532\fbidi \froman\fcharset162\fprq2 Cambria Tur;} +{\fhimajor\f31535\fbidi \froman\fcharset186\fprq2 Cambria Baltic;}{\fhimajor\f31536\fbidi \froman\fcharset163\fprq2 Cambria (Vietnamese);}{\fbimajor\f31538\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};} +{\fbimajor\f31539\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};}{\fbimajor\f31541\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};} +{\fbimajor\f31542\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};}{\fbimajor\f31543\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};} +{\fbimajor\f31544\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};}{\fbimajor\f31545\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};} +{\fbimajor\f31546\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}{\flominor\f31548\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};} +{\flominor\f31549\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};}{\flominor\f31551\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};} +{\flominor\f31552\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};}{\flominor\f31553\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};} +{\flominor\f31554\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};}{\flominor\f31555\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};} +{\flominor\f31556\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}{\fdbminor\f31558\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};} +{\fdbminor\f31559\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};}{\fdbminor\f31561\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};} +{\fdbminor\f31562\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};}{\fdbminor\f31563\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};} +{\fdbminor\f31564\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};}{\fdbminor\f31565\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};} +{\fdbminor\f31566\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}{\fhiminor\f31568\fbidi \fswiss\fcharset238\fprq2 Calibri CE;}{\fhiminor\f31569\fbidi \fswiss\fcharset204\fprq2 Calibri Cyr;} +{\fhiminor\f31571\fbidi \fswiss\fcharset161\fprq2 Calibri Greek;}{\fhiminor\f31572\fbidi \fswiss\fcharset162\fprq2 Calibri Tur;}{\fhiminor\f31575\fbidi \fswiss\fcharset186\fprq2 Calibri Baltic;} +{\fhiminor\f31576\fbidi \fswiss\fcharset163\fprq2 Calibri (Vietnamese);}{\fbiminor\f31578\fbidi \froman\fcharset238\fprq2 Times New Roman CE{\*\falt Times};}{\fbiminor\f31579\fbidi \froman\fcharset204\fprq2 Times New Roman Cyr{\*\falt Times};} +{\fbiminor\f31581\fbidi \froman\fcharset161\fprq2 Times New Roman Greek{\*\falt Times};}{\fbiminor\f31582\fbidi \froman\fcharset162\fprq2 Times New Roman Tur{\*\falt Times};} +{\fbiminor\f31583\fbidi \froman\fcharset177\fprq2 Times New Roman (Hebrew){\*\falt Times};}{\fbiminor\f31584\fbidi \froman\fcharset178\fprq2 Times New Roman (Arabic){\*\falt Times};} +{\fbiminor\f31585\fbidi \froman\fcharset186\fprq2 Times New Roman Baltic{\*\falt Times};}{\fbiminor\f31586\fbidi \froman\fcharset163\fprq2 Times New Roman (Vietnamese){\*\falt Times};}}{\colortbl;\red0\green0\blue0;\red0\green0\blue255; +\red0\green255\blue255;\red0\green255\blue0;\red255\green0\blue255;\red255\green0\blue0;\red255\green255\blue0;\red255\green255\blue255;\red0\green0\blue128;\red0\green128\blue128;\red0\green128\blue0;\red128\green0\blue128;\red128\green0\blue0; +\red128\green128\blue0;\red128\green128\blue128;\red192\green192\blue192;\red0\green51\blue0;}{\*\defchp \fs22\dbch\af11 }{\*\defpap \ql \li0\ri0\sa200\sl276\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 } +\noqfpromote {\stylesheet{\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\snext0 \sautoupd \sqformat \spriority0 Normal;}{\s1\ql \fi-357\li357\ri0\sb120\sa120\widctlpar\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls17\outlinelevel0\adjustright\rin0\lin357\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 +\b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext1 \slink15 \sqformat heading 1;}{\s2\ql \fi-363\li720\ri0\sb120\sa120\widctlpar +\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl1\outlinelevel1\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext2 \slink16 \sqformat heading 2;}{\s3\ql \fi-357\li1077\ri0\sb120\sa120\widctlpar\tx1077\jclisttab\tx1440\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl2\outlinelevel2\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \af38\afs19\alang1025 +\ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext3 \slink17 \sqformat heading 3;}{\s4\ql \fi-358\li1435\ri0\sb120\sa120\widctlpar +\jclisttab\tx1437\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl3\outlinelevel3\adjustright\rin0\lin1435\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext4 \slink18 \sqformat heading 4;}{\s5\ql \fi-357\li1792\ri0\sb120\sa120\widctlpar\tx1792\jclisttab\tx2155\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl4\outlinelevel4\adjustright\rin0\lin1792\itap0 \rtlch\fcs1 \af38\afs19\alang1025 +\ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext5 \slink19 \sqformat heading 5;}{\s6\ql \fi-357\li2149\ri0\sb120\sa120\widctlpar +\jclisttab\tx2152\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl5\outlinelevel5\adjustright\rin0\lin2149\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext6 \slink20 \sqformat heading 6;}{\s7\ql \fi-357\li2506\ri0\sb120\sa120\widctlpar\jclisttab\tx2509\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl6\outlinelevel6\adjustright\rin0\lin2506\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext7 \slink21 \sqformat heading 7;}{\s8\ql \fi-357\li2863\ri0\sb120\sa120\widctlpar +\jclisttab\tx2866\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl7\outlinelevel7\adjustright\rin0\lin2863\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext8 \slink22 \sqformat heading 8;}{\s9\ql \fi-358\li3221\ri0\sb120\sa120\widctlpar\jclisttab\tx3223\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl8\outlinelevel8\adjustright\rin0\lin3221\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext9 \slink23 \sqformat heading 9;}{\*\cs10 \additive \ssemihidden Default Paragraph Font;}{\* +\ts11\tsrowd\trftsWidthB3\trpaddl108\trpaddr108\trpaddfl3\trpaddft3\trpaddfb3\trpaddfr3\tblind0\tblindtype3\tsvertalt\tsbrdrt\tsbrdrl\tsbrdrb\tsbrdrr\tsbrdrdgl\tsbrdrdgr\tsbrdrh\tsbrdrv \ql \li0\ri0\sa200\sl276\slmult1 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af0\afs22\alang1025 \ltrch\fcs0 \fs22\lang1033\langfe1033\loch\f0\hich\af0\dbch\af11\cgrid\langnp1033\langfenp1033 \snext11 \ssemihidden \sunhideused Normal Table;}{\* +\cs15 \additive \rtlch\fcs1 \ab\af0\afs32 \ltrch\fcs0 \b\fs32\kerning32\loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink1 \slocked \spriority9 Heading 1 Char;}{\*\cs16 \additive \rtlch\fcs1 \ab\af38 \ltrch\fcs0 +\b\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\langnp1033\langfenp1033 \sbasedon10 \slink2 \slocked Heading 2 Char;}{\*\cs17 \additive \rtlch\fcs1 \ab\af0\afs26 \ltrch\fcs0 \b\fs26\loch\f31502\hich\af31502\dbch\af31501 +\sbasedon10 \slink3 \slocked \ssemihidden \spriority9 Heading 3 Char;}{\*\cs18 \additive \rtlch\fcs1 \ab\af0\afs28 \ltrch\fcs0 \b\fs28\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \slink4 \slocked \ssemihidden \spriority9 Heading 4 Char;}{\*\cs19 +\additive \rtlch\fcs1 \ab\ai\af0\afs26 \ltrch\fcs0 \b\i\fs26\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \slink5 \slocked \ssemihidden \spriority9 Heading 5 Char;}{\*\cs20 \additive \rtlch\fcs1 \ab\af0 \ltrch\fcs0 +\b\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \slink6 \slocked \ssemihidden \spriority9 Heading 6 Char;}{\*\cs21 \additive \rtlch\fcs1 \af0\afs24 \ltrch\fcs0 \fs24\loch\f31506\hich\af31506\dbch\af31505 +\sbasedon10 \slink7 \slocked \ssemihidden \spriority9 Heading 7 Char;}{\*\cs22 \additive \rtlch\fcs1 \ai\af0\afs24 \ltrch\fcs0 \i\fs24\loch\f31506\hich\af31506\dbch\af31505 \sbasedon10 \slink8 \slocked \ssemihidden \spriority9 Heading 8 Char;}{\*\cs23 +\additive \rtlch\fcs1 \af0 \ltrch\fcs0 \loch\f31502\hich\af31502\dbch\af31501 \sbasedon10 \slink9 \slocked \ssemihidden \spriority9 Heading 9 Char;}{\s24\ql \li357\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin357\itap0 +\rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext24 Body 1;}{ +\s25\ql \li720\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext25 Body 2;}{\s26\ql \li1077\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext26 Body 3;}{\s27\ql \li1435\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1435\itap0 \rtlch\fcs1 +\af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext27 Body 4;}{ +\s28\ql \li1803\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1803\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext28 Body 5;}{\s29\ql \li2160\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin2160\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext29 Body 6;}{\s30\ql \li2506\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin2506\itap0 \rtlch\fcs1 +\af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext30 Body 7;}{ +\s31\ql \li2863\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin2863\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext31 Body 8;}{\s32\ql \li3221\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin3221\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext32 Body 9;}{\s33\ql \fi-357\li357\ri0\sb120\sa120\widctlpar\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls1\adjustright\rin0\lin357\itap0 +\rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext33 Bullet 1;}{\s34\ql \fi-363\li720\ri0\sb120\sa120\widctlpar +\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext34 Bullet 2;}{ +\s35\ql \fi-357\li1077\ri0\sb120\sa120\widctlpar\jclisttab\tx1080\wrapdefault\aspalpha\aspnum\faauto\ls3\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext35 Bullet 3;}{\s36\ql \fi-358\li1435\ri0\sb120\sa120\widctlpar\jclisttab\tx1437\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin1435\itap0 +\rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext36 Bullet 4;}{\s37\ql \fi-357\li1792\ri0\sb120\sa120\widctlpar +\jclisttab\tx1795\wrapdefault\aspalpha\aspnum\faauto\ls5\adjustright\rin0\lin1792\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext37 Bullet 5;}{ +\s38\ql \fi-357\li2149\ri0\sb120\sa120\widctlpar\jclisttab\tx2152\wrapdefault\aspalpha\aspnum\faauto\ls6\adjustright\rin0\lin2149\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext38 Bullet 6;}{\s39\ql \fi-357\li2506\ri0\sb120\sa120\widctlpar\jclisttab\tx2509\wrapdefault\aspalpha\aspnum\faauto\ls7\adjustright\rin0\lin2506\itap0 +\rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext39 Bullet 7;}{\s40\ql \fi-357\li2863\ri0\sb120\sa120\widctlpar +\jclisttab\tx2866\wrapdefault\aspalpha\aspnum\faauto\ls8\adjustright\rin0\lin2863\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext40 Bullet 8;}{ +\s41\ql \fi-358\li3221\ri0\sb120\sa120\widctlpar\jclisttab\tx3223\wrapdefault\aspalpha\aspnum\faauto\ls9\adjustright\rin0\lin3221\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon32 \snext41 Bullet 9;}{\s42\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\ab\af38\afs28\alang1025 \ltrch\fcs0 \b\fs28\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 Heading EULA;}{\s43\ql \li0\ri0\sb120\sa120\widctlpar\brdrb\brdrs\brdrw10\brsp20 +\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs28\alang1025 \ltrch\fcs0 \b\fs28\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 Heading Software Title;}{ +\s44\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext44 Preamble;}{\s45\ql \li0\ri0\sb120\sa120\widctlpar\brdrb\brdrs\brdrw10\brsp20 \wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 +\b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext1 Preamble Border;}{\s46\qc \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext46 Heading Warranty;}{\s47\ql \fi-360\li360\ri0\sb120\sa120\widctlpar +\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls11\outlinelevel0\adjustright\rin0\lin360\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 +Heading 1 Warranty;}{\s48\ql \fi-360\li720\ri0\sb120\sa120\widctlpar\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls11\ilvl1\outlinelevel1\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext0 Heading 2 Warranty;}{\s49\ql \fi-357\li1077\ri0\sb120\sa120\widctlpar +\tx1077\jclisttab\tx1440\wrapdefault\aspalpha\aspnum\faauto\ls10\ilvl2\outlinelevel2\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon3 \snext49 Heading 3 Bold;}{\s50\ql \fi-358\li1435\ri0\sb120\sa120\widctlpar\jclisttab\tx1437\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin1435\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\ul\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon36 \snext50 Bullet 4 Underline;}{\s51\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\af38\afs19\alang1025 \ltrch\fcs0 \fs19\ul\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon35 \snext51 Bullet 3 Underline;}{ +\s52\ql \li720\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin720\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\ul\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon25 \snext52 Body 2 Underline;}{\s53\ql \li1077\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin1077\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\ul\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon26 \snext53 Body 3 Underline;}{\s54\ql \li0\ri0\sb120\sa120\sl480\slmult1\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 +\rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext54 \slink55 Body Text Indent;}{\*\cs55 \additive \rtlch\fcs1 \af38\afs19 \ltrch\fcs0 \f38\fs19 +\sbasedon10 \slink54 \slocked \ssemihidden Body Text Indent Char;}{\s56\ql \fi-358\li1435\ri0\sb120\sa120\widctlpar\jclisttab\tx1437\wrapdefault\aspalpha\aspnum\faauto\ls4\adjustright\rin0\lin1435\itap0 \rtlch\fcs1 \ai\af38\afs19\alang1025 \ltrch\fcs0 +\i\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon36 \snext56 Bullet 4 Italics;}{\*\cs57 \additive \rtlch\fcs1 \af38 \ltrch\fcs0 \f38\lang1033\langfe1033\langnp1033\langfenp1033 \sbasedon10 Body 2 Char;}{\* +\cs58 \additive \rtlch\fcs1 \af38 \ltrch\fcs0 \f38\lang1033\langfe1033\langnp1033\langfenp1033 \sbasedon10 Body 3 Char;}{\*\cs59 \additive \rtlch\fcs1 \af38 \ltrch\fcs0 \f38\lang1033\langfe1033\langnp1033\langfenp1033 \sbasedon10 Body 4 Char;}{\*\cs60 +\additive \rtlch\fcs1 \af38 \ltrch\fcs0 \f38\lang1033\langfe1033\langnp1033\langfenp1033 \sbasedon10 Body 1 Char;}{\s61\ql \li0\ri0\sb120\sa120\widctlpar\brdrt\brdrs\brdrw10\brsp20 \wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 +\rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon44 \snext61 Preamble Border Above;}{ +\s62\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext62 \slink63 \ssemihidden footnote text;}{\*\cs63 \additive \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \f38\fs20 \sbasedon10 \slink62 \slocked \ssemihidden Footnote Text Char;}{\*\cs64 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \super +\sbasedon10 \ssemihidden footnote reference;}{\s65\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext65 \slink66 \ssemihidden endnote text;}{\*\cs66 \additive \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \f38\fs20 \sbasedon10 \slink65 \slocked \ssemihidden +Endnote Text Char;}{\*\cs67 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \super \sbasedon10 \ssemihidden endnote reference;}{\s68\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 +\ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext68 \slink69 \ssemihidden annotation text;}{\*\cs69 \additive \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \f38\fs20 +\sbasedon10 \slink68 \slocked \ssemihidden Comment Text Char;}{\*\cs70 \additive \rtlch\fcs1 \af0\afs16 \ltrch\fcs0 \fs16 \sbasedon10 \ssemihidden annotation reference;}{\s71\ql \li0\ri0\sa160\sl-240\slmult0 +\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext71 Char;}{ +\s72\ql \li0\ri0\sa160\sl-240\slmult0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext72 Char Char Char Char;}{\*\cs73 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \ul\cf2 \sbasedon10 Hyperlink;}{\s74\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\af38\afs16\alang1025 \ltrch\fcs0 \fs16\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext74 \slink75 \ssemihidden Balloon Text;}{\*\cs75 \additive \rtlch\fcs1 \af38\afs16 \ltrch\fcs0 \f38\fs16 +\sbasedon10 \slink74 \slocked \ssemihidden Balloon Text Char;}{\*\cs76 \additive \rtlch\fcs1 \ab\af39 \ltrch\fcs0 \b\f39\lang1033\langfe1033\langnp1033\langfenp1033 \sbasedon10 Heading 2 Char1;}{\*\cs77 \additive \rtlch\fcs1 \af0 \ltrch\fcs0 \sbasedon10 +page number;}{\s78\ql \li0\ri0\sa160\sl-240\slmult0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \sbasedon0 \snext78 Char Char Char Char1;}{\s79\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs19\alang1025 +\ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \snext0 \styrsid8999754 Body 0 Bold;}{\s80\ql \li0\ri0\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 +\af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 \snext0 \styrsid8999754 Body 0;}{\s81\ql \li0\ri0\sb120\sa120\widctlpar +\tqc\tx4320\tqr\tx8640\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext81 \slink82 \styrsid11496811 header;}{\*\cs82 \additive \rtlch\fcs1 \af38\afs19 \ltrch\fcs0 \f38\fs19 \sbasedon10 \slink81 \slocked \ssemihidden Header Char;}{\s83\ql \li0\ri0\sb120\sa120\widctlpar +\tqc\tx4320\tqr\tx8640\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon0 \snext83 \slink84 \styrsid11496811 footer;}{\*\cs84 \additive \rtlch\fcs1 \af38\afs19 \ltrch\fcs0 \f38\fs19 \sbasedon10 \slink83 \slocked \ssemihidden Footer Char;}{ +\s85\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \ab\af38\afs20\alang1025 \ltrch\fcs0 \b\fs20\lang1033\langfe1033\loch\f38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +\sbasedon68 \snext68 \slink86 \ssemihidden \sunhideused \styrsid8850911 annotation subject;}{\*\cs86 \additive \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\f38\fs20 \sbasedon69 \slink85 \slocked \ssemihidden \styrsid8850911 Comment Subject Char;}} +{\*\listtable{\list\listtemplateid176468498\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid692200086\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s41\fi-358\li3221 +\jclisttab\tx3223\lin3221 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23 +\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691 +\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;} +\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid196815738}{\list\listtemplateid-1793664660{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers +\'01;}\rtlch\fcs1 \ab\ai0\af0 \ltrch\fcs0 \b\i0\fbias0 \s47\fi-360\li360\jclisttab\tx360\lin360 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab\ai0\af0 \ltrch\fcs0 \b\i0\fbias0 \s48\fi-360\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 +\fbias0 \fi-360\li1080\jclisttab\tx1080\lin1080 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'03);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li1440 +\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'04);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li1800\jclisttab\tx1800\lin1800 } +{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'05);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc0 +\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li2520\jclisttab\tx2520\lin2520 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fbias0 \fi-360\li3240\jclisttab\tx3240\lin3240 }{\listname ;}\listid394402059}{\list\listtemplateid1928476992{\listlevel\levelnfc0\levelnfcn0\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0 \s49\fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1 +\levelnfcn1\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-357\li1792\jclisttab\tx2155\lin1792 } +{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2149\jclisttab\tx2152\lin2149 } +{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2506\jclisttab\tx2509\lin2506 } +{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2863\jclisttab\tx2866\lin2863 } +{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-358\li3221\jclisttab\tx3223\lin3221 } +{\listname ;}\listid398796681}{\list\listtemplateid789093748\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid-317712510\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 +\s34\fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440 +\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid477573462}{\list\listtemplateid1948578256{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af0\afs20 \ltrch\fcs0 \b\i0\fs20\fbias0 \fi-360\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid630479929}{\list\listtemplateid67698717 +{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li360\jclisttab\tx360\lin360 }{\listlevel\levelnfc4\levelnfcn4\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'02\'02);}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li1080\jclisttab\tx1080\lin1080 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'03(\'03);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'04);}{\levelnumbers\'02;} +\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li1800\jclisttab\tx1800\lin1800 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'03(\'05);}{\levelnumbers\'02;}\rtlch\fcs1 \af0 \ltrch\fcs0 +\fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2520\jclisttab\tx2520\lin2520 +}{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'07.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc2\levelnfcn2 +\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'08.;}{\levelnumbers\'01;}\rtlch\fcs1 \af0 \ltrch\fcs0 \fi-360\li3240\jclisttab\tx3240\lin3240 }{\listname ;}\listid700712945}{\list\listtemplateid-53848358{\listlevel +\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0 \s1\fi-357\li357\jclisttab\tx360\lin357 }{\listlevel +\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0 \s2\fi-363\li720\jclisttab\tx720\lin720 }{\listlevel +\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0 \s3\fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel +\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \s4\fi-358\li1435 +\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 +\s5\fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 +\b0\i0\f39\fs20\fbias0 \s6\fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 +\ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \s7\fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 +\ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \s8\fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 +\ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \s9\fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid752163927}{\list\listtemplateid2088029282{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0 \fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid800729109} +{\list\listtemplateid-296591990\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s40\fi-357\li2863\jclisttab\tx2866\lin2863 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 +\fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid810947713}{\list\listtemplateid1567531878{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-363\li720\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li1077\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid826823576} +{\list\listtemplateid2088029282{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'00.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-357\li357 +\jclisttab\tx360\lin357 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'01.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af39\afs20 \ltrch\fcs0 \b\i0\f39\fs20\fbias0 \fi-363\li720 +\jclisttab\tx720\lin720 }{\listlevel\levelnfc2\levelnfcn2\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'02.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab\ai0\af38\afs20 \ltrch\fcs0 \b\i0\f38\fs20\fbias0 \fi-357\li1077 +\jclisttab\tx1440\lin1077 }{\listlevel\levelnfc3\levelnfcn3\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'03.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\strike0\f39\fs20\ulnone\fbias0 +\fi-358\li1435\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc1\levelnfcn1\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'04.;}{\levelnumbers\'01;}\rtlch\fcs1 \ab0\ai0\af39\afs20 \ltrch\fcs0 +\b0\i0\strike0\f39\fs20\ulnone\fbias0 \fi-357\li1792\jclisttab\tx2155\lin1792 }{\listlevel\levelnfc0\levelnfcn0\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'05.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc4\levelnfcn4\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02\'06.;}{\levelnumbers\'01;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02i.;}{\levelnumbers;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-357\li2863\jclisttab\tx2866\lin2863 }{\listlevel\levelnfc255\levelnfcn255\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'02A.;}{\levelnumbers;}\rtlch\fcs1 +\ab0\ai0\af39\afs20 \ltrch\fcs0 \b0\i0\f39\fs20\fbias0 \fi-358\li3221\jclisttab\tx3223\lin3221 }{\listname ;}\listid974869818}{\list\listtemplateid-1813845996\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s39\fi-357\li2506\jclisttab\tx2509\lin2506 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160 +\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760 +\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname +;}\listid1219436735}{\list\listtemplateid-41362566\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s36\fi-358\li1435 +\jclisttab\tx1437\lin1435 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23 +\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320 +\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace0\levelindent0{\leveltext\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1559511898}{\list\listtemplateid-743794326\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid2033377338\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s35\fi-357\li1077\jclisttab\tx1080\lin1077 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0 +{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691 +\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;} +\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040 +\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1567649130} +{\list\listtemplateid1363474438\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid-1175557160\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s37\fi-357\li1792 +\jclisttab\tx1795\lin1792 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0 +\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1 +\levelspace1077\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0 +{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace1077\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1848404271}{\list\listtemplateid-1802592190\listhybrid{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\leveltemplateid1229593488\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s38\fi-357\li2149\jclisttab\tx2152\lin2149 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;} +\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600 +\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 } +{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23 +\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0 +\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 }{\listname ;}\listid1877695764}{\list\listtemplateid1186249844\listhybrid{\listlevel +\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid1637229796\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \s33\fi-357\li357\jclisttab\tx360\lin357 }{\listlevel\levelnfc23\levelnfcn23 +\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li1440\jclisttab\tx1440\lin1440 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0 +\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li2160\jclisttab\tx2160\lin2160 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0 +\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;}\f3\fbias0 \fi-360\li2880\jclisttab\tx2880\lin2880 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext +\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li3600\jclisttab\tx3600\lin3600 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693 +\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li4320\jclisttab\tx4320\lin4320 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698689\'01\u-3913 ?;}{\levelnumbers;} +\f3\fbias0 \fi-360\li5040\jclisttab\tx5040\lin5040 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698691\'01o;}{\levelnumbers;}\f2\fbias0 \fi-360\li5760 +\jclisttab\tx5760\lin5760 }{\listlevel\levelnfc23\levelnfcn23\leveljc0\leveljcn0\levelfollow0\levelstartat1\levelspace0\levelindent0{\leveltext\leveltemplateid67698693\'01\u-3929 ?;}{\levelnumbers;}\f10\fbias0 \fi-360\li6480\jclisttab\tx6480\lin6480 } +{\listname ;}\listid2054619191}}{\*\listoverridetable{\listoverride\listid2054619191\listoverridecount0\ls1}{\listoverride\listid477573462\listoverridecount0\ls2}{\listoverride\listid1567649130\listoverridecount0\ls3}{\listoverride\listid1559511898 +\listoverridecount0\ls4}{\listoverride\listid1848404271\listoverridecount0\ls5}{\listoverride\listid1877695764\listoverridecount0\ls6}{\listoverride\listid1219436735\listoverridecount0\ls7}{\listoverride\listid810947713\listoverridecount0\ls8} +{\listoverride\listid196815738\listoverridecount0\ls9}{\listoverride\listid398796681\listoverridecount0\ls10}{\listoverride\listid394402059\listoverridecount0\ls11}{\listoverride\listid700712945\listoverridecount0\ls12}{\listoverride\listid826823576 +\listoverridecount0\ls13}{\listoverride\listid630479929\listoverridecount0\ls14}{\listoverride\listid800729109\listoverridecount0\ls15}{\listoverride\listid974869818\listoverridecount0\ls16}{\listoverride\listid752163927\listoverridecount0\ls17} +{\listoverride\listid398796681\listoverridecount0\ls18}{\listoverride\listid398796681\listoverridecount0\ls19}{\listoverride\listid477573462\listoverridecount0\ls20}}{\*\rsidtbl \rsid17701\rsid72953\rsid200783\rsid222748\rsid345491\rsid480810\rsid535495 +\rsid538148\rsid555183\rsid676065\rsid745150\rsid787759\rsid986785\rsid1006099\rsid1009112\rsid1012355\rsid1070219\rsid1122066\rsid1182701\rsid1206375\rsid1245853\rsid1342505\rsid1528414\rsid1529837\rsid1591306\rsid1660969\rsid1722062\rsid1790012 +\rsid1800865\rsid1845488\rsid1901753\rsid1987218\rsid2173756\rsid2239916\rsid2571685\rsid2584538\rsid2765700\rsid2775782\rsid2781980\rsid2784514\rsid2818569\rsid2830425\rsid2962852\rsid3042060\rsid3162620\rsid3163049\rsid3370445\rsid3411320\rsid3411753 +\rsid3416253\rsid3439038\rsid3475551\rsid3611186\rsid3689565\rsid3739474\rsid3806252\rsid3822783\rsid4022155\rsid4023230\rsid4144829\rsid4202022\rsid4259872\rsid4287357\rsid4287841\rsid4457576\rsid4595328\rsid4738534\rsid4739523\rsid4793230\rsid4814690 +\rsid4865423\rsid4878548\rsid4995346\rsid5010248\rsid5062678\rsid5140435\rsid5185544\rsid5250241\rsid5405299\rsid5450553\rsid5459775\rsid5519492\rsid5525537\rsid5660926\rsid5718733\rsid5718961\rsid5773282\rsid5788093\rsid5901771\rsid6033147\rsid6042923 +\rsid6119652\rsid6184270\rsid6227403\rsid6231754\rsid6304161\rsid6365404\rsid6373957\rsid6425843\rsid6453852\rsid6492030\rsid6498245\rsid6500924\rsid6506467\rsid6647886\rsid6758513\rsid6888647\rsid6889714\rsid6954571\rsid6971210\rsid7028642\rsid7100767 +\rsid7226971\rsid7282236\rsid7290457\rsid7345747\rsid7428746\rsid7433926\rsid7438204\rsid7495929\rsid7554964\rsid7619174\rsid7692510\rsid7754893\rsid7800249\rsid7878867\rsid8004214\rsid8132403\rsid8197303\rsid8214982\rsid8259998\rsid8324055\rsid8325040 +\rsid8419363\rsid8458805\rsid8545132\rsid8671477\rsid8679719\rsid8738620\rsid8745808\rsid8812012\rsid8850911\rsid8857738\rsid8858237\rsid8999754\rsid9071447\rsid9203972\rsid9261549\rsid9307880\rsid9321702\rsid9526807\rsid9649378\rsid9651656\rsid9664357 +\rsid9703728\rsid9778027\rsid9796909\rsid9857610\rsid9860938\rsid9861873\rsid9964378\rsid10171790\rsid10179250\rsid10294454\rsid10749433\rsid10769451\rsid10813938\rsid10882308\rsid10955317\rsid11084414\rsid11142543\rsid11347136\rsid11432977\rsid11496811 +\rsid11498068\rsid11622420\rsid11672016\rsid11686297\rsid11695497\rsid11754382\rsid11874088\rsid11882246\rsid11937164\rsid12000701\rsid12015935\rsid12020896\rsid12065226\rsid12199483\rsid12222130\rsid12255436\rsid12407360\rsid12585274\rsid12596065 +\rsid12664082\rsid12722678\rsid12797652\rsid12798176\rsid12913505\rsid13136677\rsid13309404\rsid13333597\rsid13334496\rsid13388123\rsid13828315\rsid13832939\rsid13896616\rsid13908819\rsid13965668\rsid14168694\rsid14223456\rsid14293847\rsid14297853 +\rsid14380549\rsid14382435\rsid14443673\rsid14491415\rsid14705568\rsid14771509\rsid14825379\rsid14830971\rsid14889524\rsid14894057\rsid14897950\rsid14943232\rsid15007790\rsid15427736\rsid15493712\rsid15495555\rsid15545976\rsid15601712\rsid15811431 +\rsid15822672\rsid15872081\rsid15925451\rsid16141742\rsid16202142\rsid16385696\rsid16395859\rsid16406536\rsid16450365\rsid16542934\rsid16661796\rsid16712132\rsid16716683}{\mmathPr\mmathFont34\mbrkBin0\mbrkBinSub0\msmallFrac0\mdispDef1\mlMargin0\mrMargin0 +\mdefJc1\mwrapIndent1440\mintLim0\mnaryLim1}{\info{\title MICROSOFT [SoftwareType IS "Beta Software"=PRE-RELEASE ][SoftwareType IS "Evaluation Software"=EVALUATION ]SOFTWARE LICENSE TERMS}{\creatim\yr2010\mo2\dy4\hr11\min52} +{\revtim\yr2010\mo3\dy18\hr15\min5}{\version1}{\edmins0}{\nofpages3}{\nofwords1205}{\nofchars6872}{\nofcharsws8061}{\vern49247}}{\*\userprops {\propname db_build_version}\proptype30{\staticval 2.6}{\propname db_charger_document_reference}\proptype3 +{\staticval 25146}{\propname db_charger_client_name}\proptype30{\staticval tbc}{\propname db_charger_matter_number}\proptype30{\staticval tbc}{\propname autosave}\proptype30{\staticval false}{\propname owner}\proptype30{\staticval REDMOND\'5ckathan} +{\propname db_master_reference}\proptype30{\staticval USETERMS_MAINB}{\propname db_master_version}\proptype30{\staticval 20081001}{\propname db_master_clock}\proptype3{\staticval 783}{\propname db_master_name}\proptype30{\staticval Retail/OEM Software Lic +ense Terms - Main}{\propname db_master_description}\proptype30{\staticval }{\propname db_output_filter_reference}\proptype30{\staticval }{\propname db_base_url}\proptype30{\staticval http://usetermassembly/dealbuilder_live/DealBuilderNET/dealbuilder.asp +x}{\propname ProductVersion}\proptype30{\staticval 0}{\propname MScom}\proptype11{\staticval 0}{\propname LanguageAll}\proptype30{\staticval English}{\propname CanadaAvail}\proptype11{\staticval 1}{\propname CanadaFrench}\proptype11{\staticval 0} +{\propname FileFormat}\proptype11{\staticval 0}{\propname SoftwareType}\proptype30{\staticval Beta Software}{\propname ProductName}\proptype30{\staticval Incubation Software}{\propname NumberOfCopies}\proptype30{\staticval Any number of copies} +{\propname MandatoryActivation}\proptype11{\staticval 0}{\propname BetaUseRight}\proptype30{\staticval On the user's premises}{\propname ProductKey}\proptype11{\staticval 0}{\propname ConfidentialInformation}\proptype11{\staticval 0}{\propname Feedback} +\proptype30{\staticval Optional}{\propname NetFramework}\proptype11{\staticval 0}{\propname InternetBasedServices}\proptype11{\staticval 0}{\propname InternetBasedServicesFeaturesDescOther}\proptype30{\staticval }{\propname TimeBomb}\proptype11 +{\staticval 0}{\propname TermWhen}\proptype30{\staticval A certain number of years}{\propname TermYears}\proptype30{\staticval Two}{\propname TermCommRel}\proptype11{\staticval 0}{\propname db_commit}\proptype30{\staticval ProductVersion}} +{\*\xmlnstbl {\xmlns1 http://schemas.microsoft.com/office/word/2003/wordml}}\paperw12240\paperh15840\margl720\margr720\margt720\margb720\gutter0\ltrsect +\widowctrl\ftnbj\aenddoc\trackmoves0\trackformatting1\donotembedsysfont0\relyonvml0\donotembedlingdata1\grfdocevents0\validatexml1\showplaceholdtext0\ignoremixedcontent0\saveinvalidxml0\showxmlerrors1\noxlattoyen +\expshrtn\noultrlspc\dntblnsbdb\nospaceforul\hyphcaps0\formshade\horzdoc\dgmargin\dghspace95\dgvspace180\dghorigin720\dgvorigin720\dghshow2\dgvshow1 +\jexpand\viewkind1\viewscale100\pgbrdrhead\pgbrdrfoot\splytwnine\ftnlytwnine\htmautsp\nolnhtadjtbl\useltbaln\alntblind\lytcalctblwd\lyttblrtgr\lnbrkrule\rempersonalinfo\rsidroot10813938 \fet0{\*\wgrffmtfilter 013f}\ilfomacatclnup0 +{\*\docvar {db_xml}{\'0d\'0dhttp://usetermassembly/dealbuilder_live/DealBuilderNET/dealbuilder.aspxmicrosoftmicrosoftmicrosoft25145tbctbcUSETERMS_MAINB2.6Retail/OEM Software License Terms - Main 20081001783trueuniquetrue +truetruetruetruetruetruelazyday_month_year,.day_month_year,._blank +rtffalsedraftingindefinitetrueautosave|text|falseowner|text|REDMOND +\'5ckathantruefalsetruepromptvaluepagegroup< +Value>sureunsureunknowndeferredfalsealiasfalseascendingfalsetruefalseRepeat< +Control NAME="db_input_heading_highlight_column" TYPE="string">CheckPrompt +AnswerDeferralGuidanceInsert your comments belowVariable/dealbuilder_live/help/dealb +uilder/help.htmlonsubmittruetruefalsetruefalsefalsetrue2dropdownsureUnknownfirstOtherlast20204Specify others:Specify other:11, and and/or or YesNo< +/Control>(%1 of %2)&\'3bnbsp\'3bvisibledigitsPrevNext&\'3bnbsp\'3b|&\'3bnbsp\'3b*aftera +ftertruefalseclient_side<\'3bU>\'3bWARNING:<\'3b/U>\'3b That page is no longer relevant because of answers given on this page or a previous page!enabledrelevant_pages0English100Beta SoftwareIncubation SoftwareAny number of copies0On the user&apos\'3bs premises00Optional000A certain number of yearsTwo0}}{\*\ftnsep \ltrpar \pard\plain \ltrpar +\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 +\ltrch\fcs0 \insrsid10769451 \chftnsep +\par }}{\*\ftnsepc \ltrpar \pard\plain \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid10769451 \chftnsepc +\par }}{\*\aftnsep \ltrpar \pard\plain \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid10769451 \chftnsep +\par }}{\*\aftnsepc \ltrpar \pard\plain \ltrpar\ql \li0\ri0\sb120\sa120\widctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af0 \ltrch\fcs0 \insrsid10769451 \chftnsepc +\par }}\ltrpar \sectd \ltrsect\psz1\linex0\endnhere\sectlinegrid360\sectdefaultcl\sectrsid12797652\sftnbj {\*\pnseclvl1\pnucrm\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl2\pnucltr\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl3 +\pndec\pnstart1\pnindent720\pnhang {\pntxta .}}{\*\pnseclvl4\pnlcltr\pnstart1\pnindent720\pnhang {\pntxta )}}{\*\pnseclvl5\pndec\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl6\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}} +{\*\pnseclvl7\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl8\pnlcltr\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}{\*\pnseclvl9\pnlcrm\pnstart1\pnindent720\pnhang {\pntxtb (}{\pntxta )}}\pard\plain \ltrpar +\s42\ql \li0\ri0\sb120\sa120\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid12255436 \rtlch\fcs1 \ab\af38\afs28\alang1025 \ltrch\fcs0 +\b\fs28\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 MICROSOFT PRE-RELEASE SOFTWARE LICENSE TERMS +\par }\pard\plain \ltrpar\s43\ql \li0\ri0\sb120\sa120\nowidctlpar\brdrb\brdrs\brdrw10\brsp20 \wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid12255436 \rtlch\fcs1 \ab\af38\afs28\alang1025 \ltrch\fcs0 +\b\fs28\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid8850911 \hich\af38\dbch\af13\loch\f38 IRON}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\fs20\dbch\af13\insrsid1660969 \hich\af38\dbch\af13\loch\f38 RUBY}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid8850911 \hich\af38\dbch\af13\loch\f38 TOOLS FOR VISUAL STUDIO}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\fs20\dbch\af13\insrsid10179250 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 MICROSOFT }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\fs20\dbch\af13\insrsid12255436 \hich\af38\dbch\af13\loch\f38 INCUBATION SOFTWARE}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 +\par }\pard\plain \ltrpar\s44\ql \li0\ri0\sb120\sa120\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid12255436 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 +\b\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +These license terms are an agreement between Microsoft Corporation (or based on where you live, one of its affiliates) and you.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{ +\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 Please read them.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 +\hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 They apply to the pre-release software named \hich\af38\dbch\af13\loch\f38 +above, which includes the media on which you received it, if any.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 +\b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 The terms also apply to any Microsoft +\par {\listtext\pard\plain\ltrpar \s34 \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\loch\af3\hich\af3\dbch\af13\insrsid9964378\charrsid200783 \loch\af3\dbch\af13\hich\f3 \'b7\tab}}\pard\plain \ltrpar\s34\ql \fi-360\li360\ri0\sb120\sa120\nowidctlpar +\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin360\itap0\pararsid12255436 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 +\af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 updates, +\par {\listtext\pard\plain\ltrpar \s34 \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\loch\af3\hich\af3\dbch\af13\insrsid9964378\charrsid200783 \loch\af3\dbch\af13\hich\f3 \'b7\tab}\hich\af38\dbch\af13\loch\f38 supplements, +\par {\listtext\pard\plain\ltrpar \s34 \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\loch\af3\hich\af3\dbch\af13\insrsid9964378\charrsid200783 \loch\af3\dbch\af13\hich\f3 \'b7\tab}\hich\af38\dbch\af13\loch\f38 Internet-based services, and +\par {\listtext\pard\plain\ltrpar \s34 \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\loch\af3\hich\af3\dbch\af13\insrsid9964378\charrsid200783 \loch\af3\dbch\af13\hich\f3 \'b7\tab}\hich\af38\dbch\af13\loch\f38 support services +\par }\pard\plain \ltrpar\s44\ql \li0\ri0\sb120\sa120\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid12255436 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 +\b\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +for this software, unless other terms accompany those items.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 +\b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 If so, those t\hich\af38\dbch\af13\loch\f38 erms apply. +\par }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 BY USING THE SOFTWARE, YOU ACCEPT THESE TERMS.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid12797652\charrsid200783 +\hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 IF YOU DO NOT ACCEPT THEM, DO NOT USE THE SOFTWARE. +\par }\pard\plain \ltrpar\s61\ql \li0\ri0\sb120\sa120\nowidctlpar\brdrt\brdrs\brdrw10\brsp20 \wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid12255436 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 +\b\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +If you comply with these license terms, you have the rights below. +\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\fs20\loch\af38\hich\af38\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 1.\tab}}\pard\plain \ltrpar\s1\ql \fi-357\li357\ri0\sb120\sa120\nowidctlpar +\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls17\outlinelevel0\adjustright\rin0\lin357\itap0\pararsid12255436 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 INSTALLATION AND USE RIGHTS.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid12797652\charrsid200783 +\hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s35 \rtlch\fcs1 \af38\afs19 \ltrch\fcs0 \f3\fs19\insrsid1722062\charrsid10171790 \loch\af3\dbch\af11\hich\f3 \'b7\tab}}\pard\plain \ltrpar\s35\ql \fi-357\li720\ri0\sb120\sa120\nowidctlpar +\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls3\adjustright\rin0\lin720\itap0\pararsid1722062 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38 +\ltrch\fcs0 \insrsid1722062\charrsid10171790 \hich\af38\dbch\af11\loch\f38 You may install and use any number of copies of the software on your premises }{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid1722062 \hich\af38\dbch\af11\loch\f38 +solely to evaluate and test }{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid1722062\charrsid10171790 \hich\af38\dbch\af11\loch\f38 the software}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 .}{\rtlch\fcs1 \af38\afs20 +\ltrch\fcs0 \fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid1722062\charrsid1722062 +\par {\listtext\pard\plain\ltrpar \s35 \rtlch\fcs1 \af38\afs19 \ltrch\fcs0 \f3\fs19\insrsid1722062 \loch\af3\dbch\af11\hich\f3 \'b7\tab}}{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid1722062 \hich\af38\dbch\af11\loch\f38 +These license terms do not grant you any distribution rights.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid8850911 +\par {\listtext\pard\plain\ltrpar \s35 \rtlch\fcs1 \af38\afs19 \ltrch\fcs0 \f3\fs19\insrsid8850911\charrsid8850911 \loch\af3\dbch\af11\hich\f3 \'b7\tab}}{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid8850911\charrsid8850911 \hich\af38\dbch\af11\loch\f38 This}{ +\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid9307880 \hich\af38\dbch\af11\loch\f38 }{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid8850911\charrsid8850911 \hich\af38\dbch\af11\loch\f38 software package \hich\af38\dbch\af11\loch\f38 +includes certain libraries from the standard }{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid13333597 \hich\af38\dbch\af11\loch\f38 Ruby}{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid8850911\charrsid8850911 \hich\af38\dbch\af11\loch\f38 }{\rtlch\fcs1 \af38 \ltrch\fcs0 +\insrsid13333597 \hich\af38\dbch\af11\loch\f38 1.8.6}{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid8850911\charrsid8850911 \hich\af38\dbch\af11\loch\f38 distribution, which are governed by the }{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid13333597 +\hich\af38\dbch\af11\loch\f38 Ruby}{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid8850911\charrsid8850911 \hich\af38\dbch\af11\loch\f38 }{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid13333597 \hich\af38\dbch\af11\loch\f38 1.\hich\af38\dbch\af11\loch\f38 8. +\hich\af38\dbch\af11\loch\f38 6}{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid8850911\charrsid8850911 \hich\af38\dbch\af11\loch\f38 License, a copy of which is located here:}{\rtlch\fcs1 \af38 \ltrch\fcs0 \cf17\insrsid8850911\charrsid8850911 \~ +\hich\af38\dbch\af11\loch\f38 }{\field\fldedit{\*\fldinst {\rtlch\fcs1 \af38 \ltrch\fcs0 \cf17\insrsid13333597 \hich\af38\dbch\af11\loch\f38 HYPERLINK "http://www.ruby-lang.org/en/LICENSE.txt"}{\rtlch\fcs1 \af38 \ltrch\fcs0 +\cf17\insrsid13333597\charrsid8850911 {\*\datafield +00d0c9ea79f9bace118c8200aa004ba90b0200000003000000e0c9ea79f9bace118c8200aa004ba90b6800000068007400740070003a002f002f007700770077002e0072007500620079002d006c0061006e0067002e006f00720067002f0065006e002f004c004900430045004e00530045002e0074007800740000007958 +81f43b1d7f48af2c825dc485276300000000a5ab0000}}}{\fldrslt {\rtlch\fcs1 \af38 \ltrch\fcs0 \ul\cf2\insrsid13333597 \hich\af38\dbch\af11\loch\f38 http://www.ruby-lang.org/en/LICENSE.txt}}}\sectd \ltrsect +\psz1\linex0\endnhere\sectlinegrid360\sectdefaultcl\sectrsid12797652\sftnbj {\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid8850911\charrsid1722062 +\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\fs20\loch\af38\hich\af38\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 2.\tab}}\pard\plain \ltrpar\s1\ql \fi-357\li357\ri0\sb120\sa120\nowidctlpar +\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls17\outlinelevel0\adjustright\rin0\lin357\itap0\pararsid12255436 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 TERM.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 +\ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 The term of this agreement }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid10179250 \hich\af38\dbch\af13\loch\f38 expires}{ +\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 two}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid1528414\charrsid200783 \hich\af38\dbch\af13\loch\f38 years}{ +\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid4202022 \hich\af38\dbch\af13\loch\f38 after installation of the software}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid345491\charrsid200783 .}{\rtlch\fcs1 +\af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\fs20\loch\af38\hich\af38\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 3.\tab}\hich\af38\dbch\af13\loch\f38 PRE-RELEASE SOFTWARE.}{\rtlch\fcs1 \af38\afs20 +\ltrch\fcs0 \fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 This software is a pre-release version.}{ +\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +It may not work the way a final version of the software will.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 +\b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 We may change it for the final, commercial version.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{ +\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 We also may not release a commercial version.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\fs20\loch\af38\hich\af38\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 4.\tab}\hich\af38\dbch\af13\loch\f38 FEEDBACK.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 If you give feedback about the software to +\hich\af38\dbch\af13\loch\f38 Microsoft, you give to Microsoft, without charge, the right to use, share and commercialize your feedback in any way and for any purpose.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 +\b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +You also give to third parties, without charge, any patent rights needed for their products, technologies and service\hich\af38\dbch\af13\loch\f38 s to use or interface with any specific parts of a Microsoft software or service that includes the feedback. +}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +You will not give feedback that is subject to a license that requires Microsoft to license its software or documentation to third parties because w\hich\af38\dbch\af13\loch\f38 e include your feedback in them.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 +\b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 These rights survive this agreement.}{\rtlch\fcs1 +\af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\caps\fs20\loch\af38\hich\af38\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 5.\tab}}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\caps\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 Scope of License}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 .}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 +\b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +The software is licensed, not sold. This agreement only gives you some rights to use the software.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 +\ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 Microsoft reserves all other rights.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{ +\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 Unless applicable law gives y\hich\af38\dbch\af13\loch\f38 +ou more rights despite this limitation, you may use the software only as expressly permitted in this agreement.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 +\ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 In doing so, you must comply with any technical limitations in the software that only allow you to use it in certain ways.}{\rtlch\fcs1 +\ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12722678\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 You may not}{\rtlch\fcs1 +\af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s34 \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\loch\af3\hich\af3\dbch\af13\insrsid9964378\charrsid200783 \loch\af3\dbch\af13\hich\f3 \'b7\tab}}\pard\plain \ltrpar\s34\ql \fi-363\li720\ri0\sb120\sa120\nowidctlpar +\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin720\itap0\pararsid12255436 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 +\af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 disclose the results of any benchmark tests of the software to any third party without Microsoft\hich\f38 \rquote \loch\f38 s prior written approval}{ +\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid222748 \hich\af38\dbch\af11\loch\f38 ;}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s34 \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\loch\af3\hich\af3\dbch\af13\insrsid9964378\charrsid200783 \loch\af3\dbch\af13\hich\f3 \'b7\tab}\hich\af38\dbch\af13\loch\f38 work around any technical limitations in the software} +{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid222748 \hich\af38\dbch\af11\loch\f38 ;}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s34 \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\loch\af3\hich\af3\dbch\af13\insrsid9964378\charrsid200783 \loch\af3\dbch\af13\hich\f3 \'b7\tab}\hich\af38\dbch\af13\loch\f38 +reverse engineer, decompile or disassemble the software, except and only t\hich\af38\dbch\af13\loch\f38 o the extent that applicable law expressly permits, despite this limitation}{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid222748 +\hich\af38\dbch\af11\loch\f38 ;}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s34 \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\loch\af3\hich\af3\dbch\af13\insrsid9964378\charrsid200783 \loch\af3\dbch\af13\hich\f3 \'b7\tab}\hich\af38\dbch\af13\loch\f38 +make more copies of the software than specified in this agreement or allowed by applicable law, despite this limitation}{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid222748 \hich\af38\dbch\af11\loch\f38 ;}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s34 \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\loch\af3\hich\af3\dbch\af13\insrsid9964378\charrsid200783 \loch\af3\dbch\af13\hich\f3 \'b7\tab}\hich\af38\dbch\af13\loch\f38 publish the software for others to copy}{\rtlch\fcs1 +\af38 \ltrch\fcs0 \insrsid222748 \hich\af38\dbch\af11\loch\f38 ;}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s34 \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\loch\af3\hich\af3\dbch\af13\insrsid9964378\charrsid200783 \loch\af3\dbch\af13\hich\f3 \'b7\tab}\hich\af38\dbch\af13\loch\f38 rent, lease\hich\af38\dbch\af13\loch\f38 + or lend the software}{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid222748 \hich\af38\dbch\af11\loch\f38 ;}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s34 \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\loch\af3\hich\af3\dbch\af13\insrsid9964378\charrsid200783 \loch\af3\dbch\af13\hich\f3 \'b7\tab}\hich\af38\dbch\af13\loch\f38 +transfer the software or this agreement to any third party}{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid222748 \hich\af38\dbch\af11\loch\f38 ; or}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s34 \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\loch\af3\hich\af3\dbch\af13\insrsid9964378\charrsid200783 \loch\af3\dbch\af13\hich\f3 \'b7\tab}\hich\af38\dbch\af13\loch\f38 +use the software for commercial software hosting services}{\rtlch\fcs1 \af38 \ltrch\fcs0 \insrsid222748 .}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\caps\fs20\loch\af38\hich\af38\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 6.\tab}}\pard\plain \ltrpar\s1\ql \fi-357\li357\ri0\sb120\sa120\nowidctlpar +\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls17\outlinelevel0\adjustright\rin0\lin357\itap0\pararsid12255436 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \caps\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 Export Restrictions}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 .}{\rtlch\fcs1 \ab0\af38\afs20 +\ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +The software is subject to United States export laws and regulations.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 +\b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 You must comp\hich\af38\dbch\af13\loch\f38 ly with all domestic and international export laws and regulations that apply to the software.}{\rtlch\fcs1 \ab0\af38\afs20 +\ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +These laws include restrictions on destinations, end users and end use.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 +\b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 For additional information, see }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \cs73\b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +www.microsoft.com/exporting}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 .}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \cs73\fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\caps\fs20\loch\af38\hich\af38\dbch\af13\insrsid9651656\charrsid200783 \hich\af38\dbch\af13\loch\f38 7.\tab}}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\caps\fs20\dbch\af13\insrsid9651656\charrsid200783 \hich\af38\dbch\af13\loch\f38 SUPPORT SERVICES.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9651656\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 +\b0\fs20\dbch\af13\insrsid9651656\charrsid200783 \hich\af38\dbch\af13\loch\f38 \hich\f38 Because this software is \'93\loch\f38 \hich\f38 as is,\'94\loch\f38 we may not provide support services for it.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\fs20\dbch\af13\insrsid9651656\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\caps\fs20\loch\af38\hich\af38\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 8.\tab}}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\caps\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 Entire Agreement.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 +\ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 This agreement, and the terms for supplements, updates, Internet-based services and support services that you use, are the entire agreement for the software +\hich\af38\dbch\af13\loch\f38 and support services. +\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\caps\fs20\loch\af38\hich\af38\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 9.\tab}}\pard \ltrpar\s1\ql \fi-360\li360\ri0\sb120\sa120\nowidctlpar +\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls17\outlinelevel0\adjustright\rin0\lin360\itap0\pararsid12255436 {\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \caps\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 Applicable Law}{ +\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 . +\par {\listtext\pard\plain\ltrpar \s2 \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\fs20\loch\af38\hich\af38\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 a.\tab}}\pard\plain \ltrpar\s2\ql \fi-363\li720\ri0\sb120\sa120\nowidctlpar +\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls17\ilvl1\outlinelevel1\adjustright\rin0\lin720\itap0\pararsid12255436 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 +\b\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 United States.}{\rtlch\fcs1 \ab0\af38\afs20 +\ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +If you acquired the software in the United States, Washington state law governs the interpretation of this agreement and applies to claims for breach of it, regardless of conflict of laws principle\hich\af38\dbch\af13\loch\f38 s.}{\rtlch\fcs1 +\ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +The laws of the state where you live govern all other claims, including claims under state consumer protection laws, unfair competition laws, and in tort.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s2 \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\fs20\loch\af38\hich\af38\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 b.\tab}\hich\af38\dbch\af13\loch\f38 Outside the United States.}{\rtlch\fcs1 +\ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +If you acquired the software in any other country, the laws of that \hich\af38\dbch\af13\loch\f38 country apply.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\caps\fs20\loch\af38\hich\af38\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 10.\tab}}\pard\plain \ltrpar\s1\ql \fi-357\li357\ri0\sb120\sa120\nowidctlpar +\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls17\outlinelevel0\adjustright\rin0\lin357\itap0\pararsid12255436 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 \b\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 +{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \caps\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 Legal Effect.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 } +{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 This agreement describes certain legal rights.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 +\b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +You may have other rights under the laws of your country.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 +\b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 You may also have rights with respect to the party from whom you acquired the software.}{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 +\b0\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \ab0\af38\afs20 \ltrch\fcs0 \b0\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 This agreement does not change +\hich\af38\dbch\af13\loch\f38 your rights under the laws of your country if the laws of your country do not permit it to do so.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \caps\fs20\dbch\af13\insrsid9964378\charrsid200783 +\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\caps\fs20\loch\af38\hich\af38\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 11.\tab}\hich\af38\dbch\af13\loch\f38 Disclaimer of Warranty.}{\rtlch\fcs1 +\af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \caps\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 \hich\f38 +The software is licensed \'93\loch\f38 \hich\f38 as-is.\'94}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \caps\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\caps\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 You bear the risk of using it.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \caps\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 +\af38\afs20 \ltrch\fcs0 \caps\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 Microsoft gives no express warranties, guarantees or conditions.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\caps\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \caps\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +You may have additional consumer rights under your local laws which this agreement cannot change.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \caps\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 +\ltrch\fcs0 \caps\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 To the extent permitted under your local laws, Microsoft excludes the implied warranties of merchantability, fitness for a particular purpose and non-infring +\hich\af38\dbch\af13\loch\f38 ement. +\par {\listtext\pard\plain\ltrpar \s1 \rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\caps\fs20\loch\af38\hich\af38\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 12.\tab}}\pard \ltrpar\s1\ql \fi-360\li360\ri0\sb120\sa120\nowidctlpar +\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls17\outlinelevel0\adjustright\rin0\lin360\itap0\pararsid12255436 {\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \caps\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +Limitation on and Exclusion of Remedies and Damages.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \caps\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\caps\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 You can recover from Microsoft and its suppliers only direct damages up to U.S. $5.00.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\caps\fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \caps\fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +You cannot recover any other damages, including consequential, lost profits, special, indirect or inciden\hich\af38\dbch\af13\loch\f38 tal damages. +\par }\pard\plain \ltrpar\s24\ql \li357\ri0\sb120\sa120\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin357\itap0\pararsid12255436 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 This limitation applies to +\par {\listtext\pard\plain\ltrpar \s34 \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\loch\af3\hich\af3\dbch\af13\insrsid9964378\charrsid200783 \loch\af3\dbch\af13\hich\f3 \'b7\tab}}\pard\plain \ltrpar\s34\ql \fi-363\li720\ri0\sb120\sa120\nowidctlpar +\jclisttab\tx720\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin720\itap0\pararsid12255436 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 +\af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 anything related to the software, services, content (including code) on third party Internet sites, or third party programs; and +\par {\listtext\pard\plain\ltrpar \s34 \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\loch\af3\hich\af3\dbch\af13\insrsid9964378\charrsid200783 \loch\af3\dbch\af13\hich\f3 \'b7\tab}\hich\af38\dbch\af13\loch\f38 +claims for breach of contract, breach of warranty, guarantee or condition, strict liability, negligence, or other tort to the extent permitted by applicable law. +\par }\pard\plain \ltrpar\ql \li360\ri0\sb120\sa120\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin360\itap0\pararsid12255436 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +It also applies even if Microsoft knew or should have known about the possibility of the damag\hich\af38\dbch\af13\loch\f38 es.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{ +\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid9964378\charrsid200783 \hich\af38\dbch\af13\loch\f38 +The above limitation or exclusion may not apply to you because your country may not allow the exclusion or limitation of incidental, consequential or other damages. +\par }\pard\plain \ltrpar\s79\ql \li0\ri0\sb120\sa120\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid12255436 \rtlch\fcs1 \ab\af38\afs19\alang1025 \ltrch\fcs0 +\b\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid8999754\charrsid200783 \hich\af38\dbch\af13\loch\f38 +Please note: As this software is distributed in Quebec, Canada, some of the clauses in \hich\af38\dbch\af13\loch\f38 this agreement are provided below in French. +\par \hich\af38\dbch\af13\loch\f38 Remarque}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid480810\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid8999754\charrsid200783 +\hich\af38\dbch\af13\loch\f38 \hich\f38 : Ce logiciel \'e9\loch\f38 \hich\f38 tant distribu\'e9\loch\f38 \hich\f38 au Qu\'e9\loch\f38 \hich\f38 bec, Canada, certaines des clauses dans ce contrat sont fournies ci-dessous en fran\'e7\loch\f38 ais. +\par }\pard\plain \ltrpar\s80\ql \li0\ri0\sb120\sa120\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid12255436 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\fs20\dbch\af13\insrsid8999754\charrsid200783 \hich\af38\dbch\af13\loch\f38 \hich\f38 EXON\'c9\loch\f38 RATION DE GARANTIE.}{ +\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid8999754\charrsid200783 \hich\af38\dbch\af13\loch\f38 \hich\f38 Le logiciel vis\'e9\loch\f38 \hich\f38 par une licence est offert \'ab\loch\f38 tel qu\hich\af38\dbch\af13\loch\f38 \hich\f38 el +\'bb\loch\f38 \hich\f38 . Toute utilisation de ce logiciel est \'e0\loch\f38 \hich\f38 votre seule risque et p\'e9\loch\f38 ril. Microsoft n\hich\f38 \rquote \loch\f38 \hich\f38 accorde aucune autre garantie expresse. Vous pouvez b\'e9\loch\f38 +\hich\f38 n\'e9\loch\f38 ficier de droits additionnels en vertu du }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid1070219\charrsid200783 \hich\af38\dbch\af13\loch\f38 droit local sur la protection d}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\fs20\dbch\af13\insrsid8999754\charrsid200783 \hich\af38\dbch\af13\loch\f38 es consommateurs, que ce contrat ne peut \hich\af38\dbch\af13\loch\f38 \hich\f38 modifier. La ou elles sont permises par le droit locale, les garanties implicites de qualit\'e9 +\loch\f38 marchande, d\hich\f38 \rquote \loch\f38 \hich\f38 ad\'e9\loch\f38 \hich\f38 quation \'e0\loch\f38 un usage particulier et d\hich\f38 \rquote \loch\f38 \hich\f38 absence de contrefa\'e7\loch\f38 on sont exclues. +\par }{\rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\fs20\dbch\af13\insrsid8999754\charrsid200783 \hich\af38\dbch\af13\loch\f38 \hich\f38 LIMITATION DES DOMMAGES-INT\'c9\loch\f38 \hich\f38 R\'ca\loch\f38 \hich\f38 TS ET EXCLUSION DE RESPONSABILIT\'c9\loch\f38 + POUR LES D\hich\af38\dbch\af13\loch\f38 OMMAGES.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid8999754\charrsid200783 +\hich\af38\dbch\af13\loch\f38 \hich\f38 Vous pouvez obtenir de Microsoft et de ses fournisseurs une indemnisation en cas de dommages directs uniquement \'e0\loch\f38 \hich\f38 hauteur de 5,00 $ US. Vous ne pouvez pr\'e9\loch\f38 \hich\f38 tendre \'e0 +\loch\f38 \hich\f38 aucune indemnisation pour les autres dommages, y compris les dommages sp\'e9\loch\f38 ciaux, indi\hich\af38\dbch\af13\loch\f38 \hich\f38 rects ou accessoires et pertes de b\'e9\loch\f38 \hich\f38 n\'e9\loch\f38 fices. +\par \hich\af38\dbch\af13\loch\f38 Cette limitation concerne}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid480810\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid8999754\charrsid200783 +\hich\af38\dbch\af13\loch\f38 : +\par {\listtext\pard\plain\ltrpar \s34 \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\loch\af3\hich\af3\dbch\af13\insrsid8999754\charrsid200783 \loch\af3\dbch\af13\hich\f3 \'b7\tab}}\pard\plain \ltrpar\s34\ql \fi-360\li360\ri0\sb120\sa120\nowidctlpar +\jclisttab\tx360\wrapdefault\aspalpha\aspnum\faauto\ls2\adjustright\rin0\lin360\itap0\pararsid12255436 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 \fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 +\af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid8999754\charrsid200783 \hich\af38\dbch\af13\loch\f38 tout}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 +\ltrch\fcs0 \fs20\dbch\af13\insrsid8999754\charrsid200783 \hich\af38\dbch\af13\loch\f38 \hich\f38 ce qui est reli\'e9\loch\f38 au logiciel, aux services ou au contenu (y compris le code) figurant sur des sites Internet tiers ou dans des programmes tiers} +{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid480810\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid8999754\charrsid200783 \hich\af38\dbch\af13\loch\f38 ; et +\par {\listtext\pard\plain\ltrpar \s34 \rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\loch\af3\hich\af3\dbch\af13\insrsid8999754\charrsid200783 \loch\af3\dbch\af13\hich\f3 \'b7\tab}\hich\af38\dbch\af13\loch\f38 \hich\f38 les r\'e9\hich\af38\dbch\af13\loch\f38 +\hich\f38 clamations au titre de violation de contrat ou de garantie, ou au titre de responsabilit\'e9\loch\f38 \hich\f38 stricte, de n\'e9\loch\f38 gligence ou d\hich\f38 \rquote \loch\f38 \hich\f38 une autre faute dans la limite autoris\'e9\loch\f38 +e par la loi en vigueur. +\par }\pard\plain \ltrpar\s80\ql \li0\ri0\sb120\sa120\nowidctlpar\wrapdefault\aspalpha\aspnum\faauto\adjustright\rin0\lin0\itap0\pararsid12255436 \rtlch\fcs1 \af38\afs19\alang1025 \ltrch\fcs0 +\fs19\lang1033\langfe1033\loch\af38\hich\af38\dbch\af11\cgrid\langnp1033\langfenp1033 {\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid8999754\charrsid200783 \hich\af38\dbch\af13\loch\f38 Elle s\hich\f38 \rquote \loch\f38 \hich\f38 applique +\'e9\loch\f38 \hich\f38 galement, m\'ea\loch\f38 \hich\f38 me si Microsoft connaissait ou devrait conna\'ee\hich\af38\dbch\af13\loch\f38 tre l\hich\f38 \rquote \'e9\loch\f38 \hich\f38 ventualit\'e9\loch\f38 d\hich\f38 \rquote \loch\f38 un tel dommage.}{ +\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid8999754\charrsid200783 \hich\af38\dbch\af13\loch\f38 Si votre pays n\hich\f38 +\rquote \loch\f38 autorise pas l\hich\f38 \rquote \loch\f38 \hich\f38 exclusion ou la limitation de responsabilit\'e9\loch\f38 pour les dommages indirects, accessoires ou de quelque nature que ce soit, il se peut que la limitation ou l\hich\f38 \rquote +\loch\f38 exclusion ci-dessus ne s\hich\f38 \rquote \loch\f38 appliquera\hich\af38\dbch\af13\loch\f38 \hich\f38 pas \'e0\loch\f38 \hich\f38 votre \'e9\loch\f38 gard. +\par }{\rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\fs20\dbch\af13\insrsid8999754\charrsid200783 \hich\af38\dbch\af13\loch\f38 EFFET JURIDIQUE.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{ +\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid8999754\charrsid200783 \hich\af38\dbch\af13\loch\f38 \hich\f38 Le pr\'e9\loch\f38 \hich\f38 sent contrat d\'e9\loch\f38 crit certains droits juridiques. Vous pourriez avoir d\hich\f38 \rquote +\loch\f38 \hich\f38 autres droits pr\'e9\loch\f38 vus par les lois de votre pays.}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 \fs20\dbch\af13\insrsid12797652\charrsid200783 \hich\af38\dbch\af13\loch\f38 }{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\fs20\lang1036\langfe1033\dbch\af13\langnp1036\insrsid8999754\charrsid200783 \hich\af38\dbch\af13\loch\f38 \hich\f38 Le pr\'e9\loch\f38 \hich\f38 sent contrat ne modifie pas les droits que vous conf\'e8\loch\f38 rent l}{\rtlch\fcs1 \af38\afs20 +\ltrch\fcs0 \fs20\lang1036\langfe1033\dbch\af13\langnp1036\insrsid15601712\charrsid200783 \hich\af38\dbch\af13\loch\f38 es lois de votre pays si cell\hich\af38\dbch\af13\loch\f38 es-}{\rtlch\fcs1 \af38\afs20 \ltrch\fcs0 +\fs20\lang1036\langfe1033\dbch\af13\langnp1036\insrsid8999754\charrsid200783 \hich\af38\dbch\af13\loch\f38 ci ne le permettent pas.}{\rtlch\fcs1 \ab\af38\afs20 \ltrch\fcs0 \b\fs20\lang1036\langfe1033\dbch\af13\langnp1036\insrsid9964378\charrsid200783 + +\par }{\*\themedata 504b030414000600080000002100e9de0fbfff0000001c020000130000005b436f6e74656e745f54797065735d2e786d6cac91cb4ec3301045f748fc83e52d4a +9cb2400825e982c78ec7a27cc0c8992416c9d8b2a755fbf74cd25442a820166c2cd933f79e3be372bd1f07b5c3989ca74aaff2422b24eb1b475da5df374fd9ad +5689811a183c61a50f98f4babebc2837878049899a52a57be670674cb23d8e90721f90a4d2fa3802cb35762680fd800ecd7551dc18eb899138e3c943d7e503b6 +b01d583deee5f99824e290b4ba3f364eac4a430883b3c092d4eca8f946c916422ecab927f52ea42b89a1cd59c254f919b0e85e6535d135a8de20f20b8c12c3b0 +0c895fcf6720192de6bf3b9e89ecdbd6596cbcdd8eb28e7c365ecc4ec1ff1460f53fe813d3cc7f5b7f020000ffff0300504b030414000600080000002100a5d6 +a7e7c0000000360100000b0000005f72656c732f2e72656c73848fcf6ac3300c87ef85bd83d17d51d2c31825762fa590432fa37d00e1287f68221bdb1bebdb4f +c7060abb0884a4eff7a93dfeae8bf9e194e720169aaa06c3e2433fcb68e1763dbf7f82c985a4a725085b787086a37bdbb55fbc50d1a33ccd311ba548b6309512 +0f88d94fbc52ae4264d1c910d24a45db3462247fa791715fd71f989e19e0364cd3f51652d73760ae8fa8c9ffb3c330cc9e4fc17faf2ce545046e37944c69e462 +a1a82fe353bd90a865aad41ed0b5b8f9d6fd010000ffff0300504b0304140006000800000021006b799616830000008a0000001c0000007468656d652f746865 +6d652f7468656d654d616e616765722e786d6c0ccc4d0ac3201040e17da17790d93763bb284562b2cbaebbf600439c1a41c7a0d29fdbd7e5e38337cedf14d59b +4b0d592c9c070d8a65cd2e88b7f07c2ca71ba8da481cc52c6ce1c715e6e97818c9b48d13df49c873517d23d59085adb5dd20d6b52bd521ef2cdd5eb9246a3d8b +4757e8d3f729e245eb2b260a0238fd010000ffff0300504b03041400060008000000210096b5ade296060000501b0000160000007468656d652f7468656d652f +7468656d65312e786d6cec594f6fdb3614bf0fd87720746f6327761a07758ad8b19b2d4d1bc46e871e698996d850a240d2497d1bdae38001c3ba618715d86d87 +615b8116d8a5fb34d93a6c1dd0afb0475292c5585e9236d88aad3e2412f9e3fbff1e1fa9abd7eec70c1d1221294fda5efd72cd4324f1794093b0eddd1ef62fad +79482a9c0498f184b4bd2991deb58df7dfbb8ad755446282607d22d771db8b944ad79796a40fc3585ee62949606ecc458c15bc8a702910f808e8c66c69b9565b +5d8a314d3c94e018c8de1a8fa94fd05093f43672e23d06af89927ac06762a049136785c10607758d9053d965021d62d6f6804fc08f86e4bef210c352c144dbab +999fb7b4717509af678b985ab0b6b4ae6f7ed9ba6c4170b06c788a705430adf71bad2b5b057d03606a1ed7ebf5babd7a41cf00b0ef83a6569632cd467faddec9 +699640f6719e76b7d6ac355c7c89feca9cccad4ea7d36c65b258a206641f1b73f8b5da6a6373d9c11b90c537e7f08dce66b7bbeae00dc8e257e7f0fd2badd586 +8b37a088d1e4600ead1ddaef67d40bc898b3ed4af81ac0d76a197c86826828a24bb318f3442d8ab518dfe3a20f000d6458d104a9694ac6d88728eee2782428d6 +0cf03ac1a5193be4cbb921cd0b495fd054b5bd0f530c1931a3f7eaf9f7af9e3f45c70f9e1d3ff8e9f8e1c3e3073f5a42ceaa6d9c84e5552fbffdeccfc71fa33f +9e7ef3f2d117d57859c6fffac327bffcfc793510d26726ce8b2f9ffcf6ecc98baf3efdfdbb4715f04d814765f890c644a29be408edf3181433567125272371be +15c308d3f28acd249438c19a4b05fd9e8a1cf4cd296699771c393ac4b5e01d01e5a30a787d72cf1178108989a2159c77a2d801ee72ce3a5c545a6147f32a9979 +3849c26ae66252c6ed637c58c5bb8b13c7bfbd490a75330f4b47f16e441c31f7184e140e494214d273fc80900aedee52ead87597fa824b3e56e82e451d4c2b4d +32a423279a668bb6690c7e9956e90cfe766cb37b077538abd27a8b1cba48c80acc2a841f12e698f13a9e281c57911ce298950d7e03aba84ac8c154f8655c4f2a +f074481847bd804859b5e696007d4b4edfc150b12addbecba6b18b148a1e54d1bc81392f23b7f84137c2715a851dd0242a633f900710a218ed715505dfe56e86 +e877f0034e16bafb0e258ebb4faf06b769e888340b103d3311da9750aa9d0a1cd3e4efca31a3508f6d0c5c5c398602f8e2ebc71591f5b616e24dd893aa3261fb +44f95d843b5974bb5c04f4edafb95b7892ec1108f3f98de75dc97d5772bdff7cc95d94cf672db4b3da0a6557f70db629362d72bcb0431e53c6066acac80d699a +6409fb44d08741bdce9c0e4971624a2378cceaba830b05366b90e0ea23aaa241845368b0eb9e2612ca8c742851ca251ceccc70256d8d87265dd96361531f186c +3d9058edf2c00eafe8e1fc5c509031bb4d680e9f39a3154de0accc56ae644441edd76156d7429d995bdd88664a9dc3ad50197c38af1a0c16d684060441db0256 +5e85f3b9660d0713cc48a0ed6ef7dedc2dc60b17e92219e180643ed27acffba86e9c94c78ab90980d8a9f0913ee49d62b512b79626fb06dccee2a432bbc60276 +b9f7dec44b7904cfbca4f3f6443ab2a49c9c2c41476dafd55c6e7ac8c769db1bc399161ee314bc2e75cf8759081743be1236ec4f4d6693e5336fb672c5dc24a8 +c33585b5fb9cc24e1d4885545b58463634cc5416022cd19cacfccb4d30eb45296023fd35a458598360f8d7a4003bbaae25e331f155d9d9a5116d3bfb9a95523e +51440ca2e0088dd844ec6370bf0e55d027a012ae264c45d02f708fa6ad6da6dce29c255df9f6cae0ec38666984b372ab5334cf640b37795cc860de4ae2816e95 +b21be5ceaf8a49f90b52a51cc6ff3355f47e0237052b81f6800fd7b802239daf6d8f0b1571a8426944fdbe80c6c1d40e8816b88b8569082ab84c36ff0539d4ff +6dce591a26ade1c0a7f669880485fd484582903d284b26fa4e2156cff62e4b9265844c4495c495a9157b440e091bea1ab8aaf7760f4510eaa69a6465c0e04ec6 +9ffb9e65d028d44d4e39df9c1a52ecbd3607fee9cec7263328e5d661d3d0e4f62f44acd855ed7ab33cdf7bcb8ae889599bd5c8b3029895b6825696f6af29c239 +b75a5bb1e6345e6ee6c28117e73586c1a2214ae1be07e93fb0ff51e133fb65426fa843be0fb515c187064d0cc206a2fa926d3c902e907670048d931db4c1a449 +59d366ad93b65abe595f70a75bf03d616c2dd959fc7d4e6317cd99cbcec9c58b34766661c7d6766ca1a9c1b327531486c6f941c638c67cd22a7f75e2a37be0e8 +2db8df9f30254d30c1372581a1f51c983c80e4b71ccdd28dbf000000ffff0300504b0304140006000800000021000dd1909fb60000001b010000270000007468 +656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73848f4d0ac2301484f78277086f6fd3ba109126dd88d0add40384e4 +350d363f2451eced0dae2c082e8761be9969bb979dc9136332de3168aa1a083ae995719ac16db8ec8e4052164e89d93b64b060828e6f37ed1567914b284d2624 +52282e3198720e274a939cd08a54f980ae38a38f56e422a3a641c8bbd048f7757da0f19b017cc524bd62107bd5001996509affb3fd381a89672f1f165dfe5141 +73d9850528a2c6cce0239baa4c04ca5bbabac4df000000ffff0300504b01022d0014000600080000002100e9de0fbfff0000001c020000130000000000000000 +0000000000000000005b436f6e74656e745f54797065735d2e786d6c504b01022d0014000600080000002100a5d6a7e7c0000000360100000b00000000000000 +000000000000300100005f72656c732f2e72656c73504b01022d00140006000800000021006b799616830000008a0000001c0000000000000000000000000019 +0200007468656d652f7468656d652f7468656d654d616e616765722e786d6c504b01022d001400060008000000210096b5ade296060000501b00001600000000 +000000000000000000d60200007468656d652f7468656d652f7468656d65312e786d6c504b01022d00140006000800000021000dd1909fb60000001b01000027 +00000000000000000000000000a00900007468656d652f7468656d652f5f72656c732f7468656d654d616e616765722e786d6c2e72656c73504b050600000000050005005d0100009b0a00000000} +{\*\colorschememapping 3c3f786d6c2076657273696f6e3d22312e302220656e636f64696e673d225554462d3822207374616e64616c6f6e653d22796573223f3e0d0a3c613a636c724d +617020786d6c6e733a613d22687474703a2f2f736368656d61732e6f70656e786d6c666f726d6174732e6f72672f64726177696e676d6c2f323030362f6d6169 +6e22206267313d226c743122207478313d22646b3122206267323d226c743222207478323d22646b322220616363656e74313d22616363656e74312220616363 +656e74323d22616363656e74322220616363656e74333d22616363656e74332220616363656e74343d22616363656e74342220616363656e74353d22616363656e74352220616363656e74363d22616363656e74362220686c696e6b3d22686c696e6b2220666f6c486c696e6b3d22666f6c486c696e6b222f3e} +{\*\latentstyles\lsdstimax267\lsdlockeddef0\lsdsemihiddendef1\lsdunhideuseddef1\lsdqformatdef0\lsdprioritydef99{\lsdlockedexcept \lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority0 \lsdlocked0 Normal; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 1;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 2;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 3; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 4;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 5;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 6; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 7;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 8;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority9 \lsdlocked0 heading 9; +\lsdpriority39 \lsdlocked0 toc 1;\lsdpriority39 \lsdlocked0 toc 2;\lsdpriority39 \lsdlocked0 toc 3;\lsdpriority39 \lsdlocked0 toc 4;\lsdpriority39 \lsdlocked0 toc 5;\lsdpriority39 \lsdlocked0 toc 6;\lsdpriority39 \lsdlocked0 toc 7; +\lsdpriority39 \lsdlocked0 toc 8;\lsdpriority39 \lsdlocked0 toc 9;\lsdqformat1 \lsdpriority35 \lsdlocked0 caption;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority10 \lsdlocked0 Title;\lsdpriority1 \lsdlocked0 Default Paragraph Font; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority11 \lsdlocked0 Subtitle;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority22 \lsdlocked0 Strong;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority20 \lsdlocked0 Emphasis; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority59 \lsdlocked0 Table Grid;\lsdunhideused0 \lsdlocked0 Placeholder Text;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority1 \lsdlocked0 No Spacing; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 1;\lsdunhideused0 \lsdlocked0 Revision; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority34 \lsdlocked0 List Paragraph;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority29 \lsdlocked0 Quote;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority30 \lsdlocked0 Intense Quote; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 1; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 1;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 2; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 2;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 3;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 3; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 4; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 4;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 5; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 5;\lsdsemihidden0 \lsdunhideused0 \lsdpriority60 \lsdlocked0 Light Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority61 \lsdlocked0 Light List Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority62 \lsdlocked0 Light Grid Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority63 \lsdlocked0 Medium Shading 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority64 \lsdlocked0 Medium Shading 2 Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority65 \lsdlocked0 Medium List 1 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority66 \lsdlocked0 Medium List 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority67 \lsdlocked0 Medium Grid 1 Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority68 \lsdlocked0 Medium Grid 2 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority69 \lsdlocked0 Medium Grid 3 Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority70 \lsdlocked0 Dark List Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdpriority71 \lsdlocked0 Colorful Shading Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority72 \lsdlocked0 Colorful List Accent 6;\lsdsemihidden0 \lsdunhideused0 \lsdpriority73 \lsdlocked0 Colorful Grid Accent 6; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority19 \lsdlocked0 Subtle Emphasis;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority21 \lsdlocked0 Intense Emphasis; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority31 \lsdlocked0 Subtle Reference;\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority32 \lsdlocked0 Intense Reference; +\lsdsemihidden0 \lsdunhideused0 \lsdqformat1 \lsdpriority33 \lsdlocked0 Book Title;\lsdpriority37 \lsdlocked0 Bibliography;\lsdqformat1 \lsdpriority39 \lsdlocked0 TOC Heading;}}{\*\datastore 010500000200000018000000 +4d73786d6c322e534158584d4c5265616465722e362e3000000000000000000000060000 +d0cf11e0a1b11ae1000000000000000000000000000000003e000300feff090006000000000000000000000001000000010000000000000000100000feffffff00000000feffffff0000000000000000ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +fffffffffffffffffdfffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff +ffffffffffffffffffffffffffffffff52006f006f007400200045006e00740072007900000000000000000000000000000000000000000000000000000000000000000000000000000000000000000016000500ffffffffffffffffffffffff0c6ad98892f1d411a65f0040963251e5000000000000000000000000900c +cb2ce7c6ca01feffffff00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff00000000000000000000000000000000000000000000000000000000 +00000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff0000000000000000000000000000000000000000000000000000 +000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000ffffffffffffffffffffffff000000000000000000000000000000000000000000000000 +0000000000000000000000000000000000000000000000000105000000000000}} \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyTools/source.extension.vsixmanifest b/Tools/IronStudio/IronRubyTools/source.extension.vsixmanifest new file mode 100644 index 0000000000..1c085df3fd --- /dev/null +++ b/Tools/IronStudio/IronRubyTools/source.extension.vsixmanifest @@ -0,0 +1,44 @@ + + + + IronRuby Tools for Visual Studio + Microsoft + 0.1 + IronRuby Tools for Visual Studio provides intellisense, project support, project and item templates, as well as a REPL window for IronRuby development. + 1033 + http://www.ironruby.net + IronRuby Tools for VS License.rtf + http://ironruby.codeplex.com + false + + + Ultimate + Premium + Pro + IntegratedShell + VSTS + VSTD + + + + + + + IronStudio + + + + |%CurrentProject%| + IronRuby + |%CurrentProject%| + |IronRubyToolsCore| + Templates\Projects\ConsoleAppProject + Templates\Projects\RubyOnRailsAppProject + Templates\Projects\GemProject + Templates\Projects\SinatraProject + Templates\Projects\SilverlightProject + Templates\Files\EmptyRbFile + Templates\Files\RSpecFile + Templates\Files\Rakefile + + diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore.csproj b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore.csproj new file mode 100644 index 0000000000..e647b2c071 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore.csproj @@ -0,0 +1,126 @@ + + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {B6C04406-5B05-4E66-AB36-8FD9A8D14165} + Library + Properties + Microsoft + IronRubyTools.Core + v4.0 + 512 + SAK + SAK + SAK + SAK + Client + + + true + full + false + $(SolutionDir)..\bin\Debug + DEBUG;TRACE;$(SignedSym) + prompt + 4 + true + + + pdbonly + true + $(SolutionDir)..\bin\Release + TRACE;$(SignedSym) + prompt + 4 + + + + False + $(DevEnvDir)\PublicAssemblies\Microsoft.VisualStudio.ComponentModelHost.dll + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.CoreUtility.dll + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Language.Intellisense.dll + + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Text.Data.dll + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Text.Logic.dll + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Text.UI.dll + + + + + + + + + + + + + {77323B06-15A2-4CF4-8A7A-86EAA2B66498} + IronRuby.Libraries + + + {7F6984B4-EE6D-4E6F-ABB1-E210D7DC4FDD} + Ruby + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Dynamic + + + {02FF0909-F5AD-48CF-A86A-345E721B7E40} + Microsoft.Scripting + + + {843716AE-38B3-4723-963C-950DD06BC4B8} + IronStudioCore + + + {D10C905C-7F15-41DF-9FF9-CCE461F571FD} + RemoteScriptFactory + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore.csproj.vspscc b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore.csproj.vspscc new file mode 100644 index 0000000000..feffdecaa4 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Editor/BraceMatcher.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Editor/BraceMatcher.cs new file mode 100644 index 0000000000..3ed23121f5 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Editor/BraceMatcher.cs @@ -0,0 +1,200 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.IronStudio.Core; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Language.StandardClassification; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Adornments; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; + +namespace Microsoft.IronRubyTools.Editor.Core { + /// + /// Provides highlighting of matching braces in a text view. + /// + public class BraceMatcher { + private readonly ITextView _textView; + private SimpleTagger _textMarkerTagger; + private readonly IComponentModel _compModel; + private bool _hasSpan; + private static TextMarkerTag _tag = new TextMarkerTag("bracehighlight"); + + /// + /// Starts watching the provided text view for brace matching. When new braces are inserted + /// in the text or when the cursor moves to a brace the matching braces are highlighted. + /// + public static void WatchBraceHighlights(ITextView view, IComponentModel componentModel) { + var matcher = new BraceMatcher(view, componentModel); + + // position changed only fires when the caret is explicitly moved, not from normal text edits, + // so we track both changes and position changed. + view.Caret.PositionChanged += matcher.CaretPositionChanged; + view.TextBuffer.Changed += matcher.TextBufferChanged; + } + + public BraceMatcher(ITextView view, IComponentModel componentModel) { + _textView = view; + _compModel = componentModel; + } + + private void CaretPositionChanged(object sender, CaretPositionChangedEventArgs e) { + RemoveExistingHighlights(); + + UpdateBraceMatching(e.NewPosition.BufferPosition.Position); + } + + private void TextBufferChanged(object sender, TextContentChangedEventArgs changed) { + RemoveExistingHighlights(); + + if (changed.Changes.Count == 1) { + var newText = changed.Changes[0].NewText; + if (newText == ")" || newText == "}" || newText == "]") { + UpdateBraceMatching(changed.Changes[0].NewPosition + 1); + } + } + } + + private void UpdateBraceMatching(int pos) { + if (pos != 0) { + var prevCharText = _textView.TextBuffer.CurrentSnapshot.GetText(pos - 1, 1); + if (prevCharText == ")" || prevCharText == "]" || prevCharText == "}") { + if (HighlightBrace(GetBraceKind(prevCharText), pos, -1)) { + return; + } + } + } + + if (pos != _textView.TextBuffer.CurrentSnapshot.Length) { + var nextCharText = _textView.TextBuffer.CurrentSnapshot.GetText(pos, 1); + if (nextCharText == "(" || nextCharText == "[" || nextCharText == "{") { + HighlightBrace(GetBraceKind(nextCharText), pos + 1, 1); + } + } + } + + private void RemoveExistingHighlights() { + if (_hasSpan && _textMarkerTagger != null) { + TextMarker.RemoveTagSpans(x => true); + _hasSpan = false; + } + } + + private SimpleTagger TextMarker { + get { + if (_textMarkerTagger == null) { + _textMarkerTagger = _compModel.GetService().GetTextMarkerTagger(_textView.TextBuffer); + } + return _textMarkerTagger; + } + } + + private bool HighlightBrace(BraceKind brace, int position, int direction) { + var classifier = _textView.TextBuffer.GetDlrClassifier(); + var snapshot = _textView.TextBuffer.CurrentSnapshot; + var span = new SnapshotSpan(snapshot, position - 1, 1); + var originalSpan = span; + + var spans = classifier.GetClassificationSpans(span); + // we don't highlight braces if we're in a comment or string literal + if (spans.Count == 0 || + (spans.Count == 1 && + (!spans[0].ClassificationType.IsOfType(PredefinedClassificationTypeNames.String) && + !spans[0].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Comment)))) { + + // find the opening span + var curLine = snapshot.GetLineFromPosition(position); + int curLineNo = curLine.LineNumber; + + if (direction == 1) { + span = new SnapshotSpan(snapshot, position, curLine.End.Position - position); + } else { + span = new SnapshotSpan(curLine.Start, position - curLine.Start.Position + direction); + } + + int depth = 1; + for (; ; ) { + spans = classifier.GetClassificationSpans(span); + for (int i = direction == -1 ? spans.Count - 1 : 0; i >= 0 && i < spans.Count; i += direction) { + if (IsCloseSpan(spans, i)) { + if (IsSameBraceKind(spans[i].Span.GetText(), brace)) { + depth -= direction; + } + } else if (IsOpenSpan(spans, i)) { + if (IsSameBraceKind(spans[i].Span.GetText(), brace)) { + depth += direction; + } + } + + if (depth == 0) { + _hasSpan = true; + // left brace + TextMarker.CreateTagSpan(snapshot.CreateTrackingSpan(spans[i].Span, SpanTrackingMode.EdgeExclusive), _tag); + // right brace + TextMarker.CreateTagSpan(snapshot.CreateTrackingSpan(new SnapshotSpan(snapshot, position - 1, 1), SpanTrackingMode.EdgeExclusive), _tag); + return true; + } + } + + curLineNo += direction; + if (curLineNo < 0 || curLineNo >= snapshot.LineCount) { + break; + } + + var line = snapshot.GetLineFromLineNumber(curLineNo); + span = new SnapshotSpan(line.Start, line.End); + } + } + return false; + } + + private enum BraceKind { + Bracket, + Paren, + Brace + } + + private static bool IsSameBraceKind(string brace, BraceKind kind) { + return GetBraceKind(brace) == kind; + } + + private static BraceKind GetBraceKind(string brace) { + switch (brace[0]) { + case '[': + case ']': return BraceKind.Bracket; + case '(': + case ')': return BraceKind.Paren; + case '{': + case '}': return BraceKind.Bracket; + default: throw new InvalidOperationException(); + } + } + + private static bool IsOpenSpan(IList spans, int i) { + return spans[i].ClassificationType == RubyClassifierProvider.Instance.OpenGroupingClassification || + (spans[i].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Operator) && + spans[i].Span.Length == 1 && + (spans[i].Span.GetText() == "{" || spans[i].Span.GetText() == "[")); + } + + private static bool IsCloseSpan(IList spans, int i) { + return spans[i].ClassificationType == RubyClassifierProvider.Instance.CloseGroupingClassification || + (spans[i].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Operator) && + spans[i].Span.Length == 1 && + (spans[i].Span.GetText() == "}" || spans[i].Span.GetText() == "]")); + } + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Editor/EditorExtensions.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Editor/EditorExtensions.cs new file mode 100644 index 0000000000..4731ccf43f --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Editor/EditorExtensions.cs @@ -0,0 +1,131 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.IronRubyTools.Editor.Core { + public static class EditorExtensions { + /// + /// Adds comment characters (#) to the start of each line. If there is a selection the comment is applied + /// to each selected line. Otherwise the comment is applied to the current line. + /// + /// + public static void CommentBlock(this ITextView view) { + if (view.Selection.IsActive) { + // comment every line in the selection + CommentRegion(view, view.Selection.Start.Position, view.Selection.End.Position); + } else { + // comment the current line + CommentRegion(view, view.Caret.Position.BufferPosition, view.Caret.Position.BufferPosition); + } + } + + /// + /// Removes a comment character (#) from the start of each line. If there is a selection the character is + /// removed from each selected line. Otherwise the character is removed from the current line. Uncommented + /// lines are ignored. + /// + /// + public static void UncommentBlock(this ITextView view) { + if (view.Selection.IsActive) { + // uncomment every line in the selection + UncommentRegion(view, view.Selection.Start.Position, view.Selection.End.Position); + } else { + // uncomment the current line + UncommentRegion(view, view.Caret.Position.BufferPosition, view.Caret.Position.BufferPosition); + } + + } + + private static void CommentRegion(ITextView view, SnapshotPoint start, SnapshotPoint end) { + using (var edit = view.TextBuffer.CreateEdit()) { + int minColumn = Int32.MaxValue; + // first pass, determine the position to place the comment + for (int i = start.GetContainingLine().LineNumber; i <= end.GetContainingLine().LineNumber; i++) { + var curLine = view.TextBuffer.CurrentSnapshot.GetLineFromLineNumber(i); + var text = curLine.GetText(); + + minColumn = Math.Min(GetMinimumColumn(text), minColumn); + } + + // second pass, place the comment + for (int i = start.GetContainingLine().LineNumber; i <= end.GetContainingLine().LineNumber; i++) { + var curLine = view.TextBuffer.CurrentSnapshot.GetLineFromLineNumber(i); + if (curLine.Length < minColumn) { + // need extra white space + edit.Insert(curLine.Start.Position + curLine.Length, new String(' ', minColumn - curLine.Length) + "#"); + } else { + edit.Insert(curLine.Start.Position + minColumn, "#"); + } + } + + edit.Apply(); + } + + // select the full region we just commented + UpdateSelection(view, start, end); + } + + private static int GetMinimumColumn(string text) { + for (int j = 0; j < text.Length; j++) { + if (!Char.IsWhiteSpace(text[j])) { + return j; + } + } + return Int32.MaxValue; + } + + private static void UncommentRegion(ITextView view, SnapshotPoint start, SnapshotPoint end) { + using (var edit = view.TextBuffer.CreateEdit()) { + + // first pass, determine the position to place the comment + for (int i = start.GetContainingLine().LineNumber; i <= end.GetContainingLine().LineNumber; i++) { + var curLine = view.TextBuffer.CurrentSnapshot.GetLineFromLineNumber(i); + + DeleteFirstCommentChar(edit, curLine); + } + + edit.Apply(); + } + + // select the full region we just uncommented + + UpdateSelection(view, start, end); + } + + private static void UpdateSelection(ITextView view, SnapshotPoint start, SnapshotPoint end) { + view.Selection.Select( + new SnapshotSpan( + start.GetContainingLine().Start.TranslateTo(view.TextBuffer.CurrentSnapshot, PointTrackingMode.Negative), + end.GetContainingLine().End.TranslateTo(view.TextBuffer.CurrentSnapshot, PointTrackingMode.Positive) + ), + false + ); + } + + private static void DeleteFirstCommentChar(ITextEdit edit, ITextSnapshotLine curLine) { + var text = curLine.GetText(); + for (int j = 0; j < text.Length; j++) { + if (!Char.IsWhiteSpace(text[j])) { + if (text[j] == '#') { + edit.Delete(curLine.Start.Position + j, 1); + } + break; + } + } + } + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Extensions.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Extensions.cs new file mode 100644 index 0000000000..cbafb50405 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Extensions.cs @@ -0,0 +1,54 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; + +using Microsoft.Scripting.Hosting; + +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.IronRubyTools { + static class Extensions { + internal static string GetFilePath(this ITextView textView) { + ITextDocument textDocument; + if (textView.TextBuffer.Properties.TryGetProperty(typeof(ITextDocument), out textDocument)) { + return textDocument.FilePath; + } else { + return null; + } + } + + internal static ITrackingSpan CreateTrackingSpan(this IIntellisenseSession session, ITextBuffer buffer) { + var triggerPoint = session.GetTriggerPoint(buffer); + var position = session.GetTriggerPoint(buffer).GetPosition(session.TextView.TextSnapshot); + + var snapshot = buffer.CurrentSnapshot; + if (position == snapshot.Length) { + return snapshot.CreateTrackingSpan(position, 0, SpanTrackingMode.EdgeInclusive); + } else { + return snapshot.CreateTrackingSpan(position, 1, SpanTrackingMode.EdgeInclusive); + } + } + + internal static ITrackingSpan CreateTrackingSpan0(this IIntellisenseSession session, ITextBuffer buffer) { + var triggerPoint = session.GetTriggerPoint(buffer); + var position = session.GetTriggerPoint(buffer).GetPosition(session.TextView.TextSnapshot); + + var snapshot = buffer.CurrentSnapshot; + return snapshot.CreateTrackingSpan(position, 0, SpanTrackingMode.EdgeInclusive); + } + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/IRubyRuntimeHost.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/IRubyRuntimeHost.cs new file mode 100644 index 0000000000..890a84b1bc --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/IRubyRuntimeHost.cs @@ -0,0 +1,37 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Hosting; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronRubyTools { + public interface IRubyRuntimeHost { + ScriptEngine RubyScriptEngine { + get; + } + + /// + /// The content type for the IronRuby content. + /// + IContentType ContentType { + get; + } + + bool EnterOutliningModeOnOpen { + get; + set; + } + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/AnalysisItem.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/AnalysisItem.cs new file mode 100644 index 0000000000..4f348a4003 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/AnalysisItem.cs @@ -0,0 +1,106 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using IronRuby.Compiler.Ast; +using Microsoft.VisualStudio.Text; +using System.Diagnostics; + +namespace Microsoft.IronRubyTools.Intellisense { + /// + /// One AnalysisItem is created for each file to be analyzed. This object + /// is moved onto the AnalysisEngine queue as needed. + /// + /// _state, _textBuffer and DeferTime should only be updated from within the + /// engine's lock + /// + public class AnalysisItem { + private ProjectEntry _entry; + private IAnalysisCookie _curCookie; + private SourceUnitTree _node; + + internal AnalysisItem(ProjectEntry entry) { + _entry = entry; + //_state = ItemState.Prepare; + } + + public SourceUnitTree CurrentTree { + get { + return _node; + } + } + + public void UpdateTree(SourceUnitTree newAst, IAnalysisCookie newCookie) { + lock (this) { + _node = newAst; + _curCookie = newCookie; + } + var newParse = OnNewParseTree; + if (newParse != null) { + newParse(this, EventArgs.Empty); + } + } + + public void GetTreeAndCookie(out SourceUnitTree tree, out IAnalysisCookie cookie) { + lock (this) { + tree = _node; + cookie = _curCookie; + } + } + + public void Analyze() { + _entry.Cookie = _curCookie; + + _entry.Prepare(_node); + _entry.Parse(); + + var newAnalysis = OnNewAnalysis; + if (newAnalysis != null) { + newAnalysis(this, EventArgs.Empty); + } + } + + public string Path { + get { return _entry.FilePath; } + } + + //internal ProjectEntry Entry { + // get { return _entry; } + //} + + //public bool HasAnalysis { + // get { + // return _entry != null && _entry.CurrentAnalysis != null; + // } + //} + + public event EventHandler OnNewParseTree; + public event EventHandler OnNewAnalysis; + + public override string ToString() { + return String.Format("AnalysisItem({0})", Path); + } + + internal static bool TryGetAnalysis(ITextBuffer/*!*/ buffer, out AnalysisItem analysis) { + return buffer.Properties.TryGetProperty(typeof(AnalysisItem), out analysis); + } + + internal static AnalysisItem/*!*/ GetAnalysis(ITextBuffer/*!*/ buffer) { + AnalysisItem res; + buffer.Properties.TryGetProperty(typeof(AnalysisItem), out res); + Debug.Assert(res != null); + return res; + } + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/FileCookie.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/FileCookie.cs new file mode 100644 index 0000000000..e58bc2d873 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/FileCookie.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text; +using System.IO; + +namespace Microsoft.IronRubyTools.Intellisense { + class FileCookie : IAnalysisCookie { + private readonly string _path; + private string[] _allLines; + + public FileCookie(string path) { + _path = path; + } + + public string Path { + get { + return _path; + } + } + + #region IFileCookie Members + + public string GetLine(int lineNo) { + if (_allLines == null) { + try { + _allLines = File.ReadAllLines(Path); + } catch (IOException) { + _allLines = new string[0]; + } + } + + if (lineNo - 1 < _allLines.Length) { + return _allLines[lineNo - 1]; + } + + return String.Empty; + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/IAnalysisCookie.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/IAnalysisCookie.cs new file mode 100644 index 0000000000..f4412a6fd0 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/IAnalysisCookie.cs @@ -0,0 +1,28 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.IronRubyTools.Intellisense { + /// + /// Used to track information about where the analysis came from and + /// get back the original content. + /// + public interface IAnalysisCookie { + string GetLine(int lineNo); + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/LocationInfo.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/LocationInfo.cs new file mode 100644 index 0000000000..fce7ca3e84 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/LocationInfo.cs @@ -0,0 +1,79 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + + +using System; +namespace Microsoft.IronRubyTools.Intellisense { + public class LocationInfo : IEquatable { + private readonly int _line, _column, _length; + private readonly ProjectEntry _entry; + + public LocationInfo(ProjectEntry entry, int line, int column, int length) { + _entry = entry; + _line = line; + _column = column; + _length = length; + } + + public ProjectEntry ProjectEntry { + get { + return _entry; + } + } + + public IAnalysisCookie Cookie { + get { return _entry.Cookie; } + } + + public string FilePath { + get { return _entry.FilePath; } + } + + public int Line { + get { return _line; } + } + + public int Column { + get { + return _column; + } + } + + public int Length { + get { + return _length; + } + } + + public override bool Equals(object obj) { + LocationInfo other = obj as LocationInfo; + if (other != null) { + return Equals(other); + } + return false; + } + + public override int GetHashCode() { + return Line.GetHashCode() ^ ProjectEntry.GetHashCode(); + } + + public bool Equals(LocationInfo other) { + // currently we filter only to line & file - so we'll only show 1 ref per each line + // This works nicely for get and call which can both add refs and when they're broken + // apart you still see both refs, but when they're together you only see 1. + return Line == other.Line && + ProjectEntry == other.ProjectEntry; + } + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/ProjectEntry.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/ProjectEntry.cs new file mode 100644 index 0000000000..e369203634 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/ProjectEntry.cs @@ -0,0 +1,76 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronRuby.Compiler.Ast; +using Microsoft.Scripting; +using Microsoft.Scripting.Library; + +namespace Microsoft.IronRubyTools.Intellisense { + public class ProjectEntry { + private SourceUnit _sourceUnit; + private readonly string _filePath; + private IAnalysisCookie _cookie; + private Node _tree; + private CollectingErrorSink _errorSink; + + internal ProjectEntry(SourceUnit sourceUnit, string filePath, IAnalysisCookie cookie) { + _sourceUnit = sourceUnit; + _filePath = filePath; + _cookie = cookie; + } + + public void Parse() { + if (_tree == null) { + return; + } + } + + public void Prepare() { + _errorSink = new CollectingErrorSink(); + // TODO: + //using (var parser = Utils.CreateParser(_sourceUnit, _errorSink)) { + // Prepare(parser.ParseFile(true)); + //} + } + + public void Prepare(Node tree) { + // TODO: + //_tree = tree; + //var walker = new OverviewWalker(this, unit); + //_tree.Walk(walker); + //_scopeTree = walker.ScopeTree; + } + + internal Node Tree { + get { return _tree; } + } + + public string FilePath { + get { return _filePath; } + } + + public void ReplaceSourceUnit(SourceUnit sourceUnit) { + // TODO: thread-safety? + _sourceUnit = sourceUnit; + _tree = null; + } + + // TODO: thread-safety? + public IAnalysisCookie Cookie { + get { return _cookie; } + set { _cookie = value; } + } + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/RubyAnalyzer.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/RubyAnalyzer.cs new file mode 100644 index 0000000000..aed0ceb15f --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/RubyAnalyzer.cs @@ -0,0 +1,215 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Threading; +using IronRuby.Compiler; +using IronRuby.Compiler.Ast; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Core; +using Microsoft.IronStudio.Intellisense; +using Microsoft.IronStudio.Library; +using Microsoft.IronStudio.Library.Intellisense; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Hosting.Providers; +using Microsoft.Scripting.Library; +using Microsoft.Scripting.Runtime; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Adornments; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; + +namespace Microsoft.IronRubyTools.Intellisense { + /// + /// Performs centralized parsing and analysis of Python source code. + /// + public class RubyAnalyzer : IParser, IAnalyzer { + private readonly ParseQueue _queue; + private readonly ScriptEngine _engine; + private readonly IErrorProviderFactory _squiggleProvider; + + public RubyAnalyzer(IComponentModel/*!*/ componentModel) { + _engine = componentModel.GetService().RubyScriptEngine; + _squiggleProvider = componentModel.GetService(); + _queue = new ParseQueue(this); + + _analysisQueue = new AnalysisQueue(this); + _projectFiles = new Dictionary(StringComparer.OrdinalIgnoreCase); + } + + private readonly AnalysisQueue _analysisQueue; + private readonly Dictionary _projectFiles; + private bool _implicitProject = true; + + public bool ImplicitProject { + get { + return _implicitProject; + } + set { + _implicitProject = value; + } + } + + public AnalysisItem AnalyzeTextView(ITextView textView) { + // Get an AnalysisItem for this file, creating one if necessary + var res = textView.TextBuffer.Properties.GetOrCreateSingletonProperty(() => { + string path = textView.GetFilePath(); + AnalysisItem item; + if (path != null && _projectFiles.TryGetValue(path, out item)) { + return item; + } + + var initialSnapshot = textView.TextBuffer.CurrentSnapshot; + var entry = new ProjectEntry( + SnapshotTextContentProvider.Make(_engine, initialSnapshot, path), + textView.GetFilePath(), + new SnapshotCookie(initialSnapshot) + ); + + item = new AnalysisItem(entry); + if (path != null) { + _projectFiles[path] = item; + + if (ImplicitProject) { + AddImplicitFiles(Path.GetDirectoryName(Path.GetFullPath(path))); + } + } + + return item; + }); + + // kick off initial processing on the ITextWindow + _queue.EnqueueBuffer(textView); + + return res; + } + + private void AddImplicitFiles(string dir) { + foreach (string filename in Directory.GetFiles(dir, "*.rb")) { + AnalyzeFile(filename); + } + } + + public AnalysisItem AnalyzeFile(string path) { + AnalysisItem item; + if (!_projectFiles.TryGetValue(path, out item)) { + var entry = new ProjectEntry(FileTextContentProvider.Make(_engine, path), path, null); + + _projectFiles[path] = item = new AnalysisItem(entry); + } + + _queue.EnqueueFile(path); + + return item; + } + + public void Analyze(AnalysisItem content) { + content.Analyze(); + } + + public bool IsAnalyzing { + get { + return _queue.IsParsing || _analysisQueue.IsAnalyzing; + } + } + + #region IParser Members + + public void Parse(TextContentProvider/*!*/ content) { + var errorSink = new CollectingErrorSink(); + SourceUnitTree ast = MakeParseTree(content, errorSink); + + ISnapshotTextContentProvider snapshotContent = content as ISnapshotTextContentProvider; + if (snapshotContent != null) { + // queue analysis of the parsed tree at High Pri so the active buffer is quickly re-analyzed + var snapshot = snapshotContent.Snapshot; + + var analysis = AnalysisItem.GetAnalysis(snapshot.TextBuffer); + + // only update the AST when we're error free, this way we don't remove + // a useful analysis with an incomplete and useless analysis. + if (errorSink.Errors.Count == 0) { + analysis.UpdateTree(ast, new SnapshotCookie(snapshot)); + _analysisQueue.Enqueue(analysis, AnalysisPriority.High); + } + + SimpleTagger squiggles = _squiggleProvider.GetErrorTagger(snapshot.TextBuffer); + squiggles.RemoveTagSpans(x => true); + + // update squiggles for the live buffer + foreach (ErrorResult warning in errorSink.Warnings) { + var span = warning.Span; + var tspan = CreateSpan(snapshot, span); + squiggles.CreateTagSpan(tspan, new ErrorTag("Warning", warning.Message)); + } + + foreach (ErrorResult error in errorSink.Errors) { + var span = error.Span; + var tspan = CreateSpan(snapshot, span); + squiggles.CreateTagSpan(tspan, new ErrorTag("Error", error.Message)); + } + } else { + FileTextContentProvider fileContent = content as FileTextContentProvider; + AnalysisItem analysis; + if (fileContent != null && _projectFiles.TryGetValue(fileContent.Path, out analysis)) { + analysis.UpdateTree(ast, new FileCookie(fileContent.Path)); + _analysisQueue.Enqueue(analysis, AnalysisPriority.Normal); + } + } + } + + private SourceUnitTree MakeParseTree(TextContentProvider/*!*/ content, ErrorSink/*!*/ errorSink) { + var source = new SourceUnit(HostingHelpers.GetLanguageContext(_engine), content, null, SourceCodeKind.File); + var options = new RubyCompilerOptions(); + var parser = new Parser(); + try { + int attempts = 10; + while (true) { + try { + return parser.Parse(source, options, errorSink); + } catch (IOException) { + // file being copied, try again... + if (attempts > 0) { + Thread.Sleep(100); + attempts--; + } else { + throw; + } + } + } + } catch (Exception e) { + Debug.Assert(false, String.Format("Failure in IronRuby parser: {0}", e.ToString())); + return null; + } + } + + private static ITrackingSpan CreateSpan(ITextSnapshot snapshot, SourceSpan span) { + var tspan = snapshot.CreateTrackingSpan( + new Span( + span.Start.Index, + Math.Min(span.End.Index - span.Start.Index, Math.Max(snapshot.Length - span.Start.Index, 0)) + ), + SpanTrackingMode.EdgeInclusive + ); + return tspan; + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/SnapshotCookie.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/SnapshotCookie.cs new file mode 100644 index 0000000000..0cfd8ab196 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Intellisense/SnapshotCookie.cs @@ -0,0 +1,44 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronRubyTools.Intellisense { + class SnapshotCookie : IAnalysisCookie { + private readonly ITextSnapshot _snapshot; + + public SnapshotCookie(ITextSnapshot snapshot) { + _snapshot = snapshot; + } + + public ITextSnapshot Snapshot { + get { + return _snapshot; + } + } + + #region IFileCookie Members + + public string GetLine(int lineNo) { + return _snapshot.GetLineFromLineNumber(lineNo - 1).GetText(); + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Navigation/AstScopeNode.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Navigation/AstScopeNode.cs new file mode 100644 index 0000000000..5d4e8fe429 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Navigation/AstScopeNode.cs @@ -0,0 +1,77 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronRuby.Compiler.Ast; +using Microsoft.IronStudio.Navigation; +using Microsoft.Scripting; +using Microsoft.IronRubyTools.Intellisense; + +namespace Microsoft.IronRubyTools.Navigation { + internal sealed class AstScopeNode : IScopeNode { + private readonly SourceUnitTree _ast; + private readonly ProjectEntry _projectEntry; + + public AstScopeNode(SourceUnitTree ast, ProjectEntry projectEntry) { + _ast = ast; + _projectEntry = projectEntry; + } + + #region IScopeNode Members + + public bool IsFunction { + get { return false; } + } + + public string Name { + get { return "TODO: source unit name"; } // _ast.Name; + } + + public string Description { + get { return "TODO: source unit description"; } //_ast.Documentation; + } + + public SourceLocation Start { + get { return _ast.Location.Start; } + } + + public SourceLocation End { + get { return _ast.Location.End; } + } + + public IEnumerable NestedScopes { + get { + // TODO: + return EnumerateBody(_ast.Statements); + } + } + + internal static IEnumerable EnumerateBody(Statements body) { + // TODO: + foreach (Expression expr in body) { + ModuleDefinition moduleDef = expr as ModuleDefinition; + if (moduleDef != null) { + yield return new ModuleScopeNode(moduleDef); + } + + MethodDefinition methodDef = expr as MethodDefinition; + if (methodDef != null) { + yield return new MethodScopeNode(methodDef); + } + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Navigation/MethodScopeNode.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Navigation/MethodScopeNode.cs new file mode 100644 index 0000000000..7ae370c9ba --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Navigation/MethodScopeNode.cs @@ -0,0 +1,65 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using Microsoft.IronStudio.Navigation; +using Microsoft.Scripting; +using IronRuby.Compiler.Ast; + +namespace Microsoft.IronRubyTools.Navigation { + internal sealed class MethodScopeNode : IScopeNode { + private readonly MethodDefinition _method; + + public MethodScopeNode(MethodDefinition method) { + _method = method; + } + + public MethodDefinition Definition { + get { + return _method; + } + } + + #region IScopeNode Members + + public bool IsFunction { + get { return true; } + } + + public string Name { + get { return _method.Name; } + } + + public string Description { + get { return _method.Name; } + } + + public SourceLocation Start { + get { return _method.Location.Start; } + } + + public SourceLocation End { + get { return _method.Location.End; } + } + + public IEnumerable NestedScopes { + get { + // TODO: + return AstScopeNode.EnumerateBody(_method.Body.Statements); + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Navigation/ModuleScopeNode.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Navigation/ModuleScopeNode.cs new file mode 100644 index 0000000000..4ff96a80a1 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Navigation/ModuleScopeNode.cs @@ -0,0 +1,59 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using IronRuby.Compiler.Ast; +using Microsoft.IronStudio.Navigation; +using Microsoft.Scripting; + +namespace Microsoft.IronRubyTools.Navigation { + internal sealed class ModuleScopeNode : IScopeNode { + private readonly ModuleDefinition _definition; + + public ModuleScopeNode(ModuleDefinition definition) { + _definition = definition; + } + + #region IScopeNode Members + + public bool IsFunction { + get { return false; } + } + + public string Name { + get { return "TODO: name"; } + } + + public string Description { + get { return "TODO: description"; } + } + + public SourceLocation Start { + get { return _definition.Location.Start; } + } + + public SourceLocation End { + get { return _definition.Location.End; } + } + + public IEnumerable NestedScopes { + get { + // TODO: + return AstScopeNode.EnumerateBody(_definition.Body.Statements); + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/OutliningTaggerProvider.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/OutliningTaggerProvider.cs new file mode 100644 index 0000000000..66ad70bff1 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/OutliningTaggerProvider.cs @@ -0,0 +1,185 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Diagnostics; +using IronRuby.Compiler.Ast; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Core; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronRubyTools { + [Export(typeof(ITaggerProvider)), ContentType(RubyCoreConstants.ContentType)] + [TagType(typeof(IOutliningRegionTag))] + class OutliningTaggerProvider : ITaggerProvider { + private readonly IRubyRuntimeHost _host; + + [ImportingConstructor] + public OutliningTaggerProvider(IRubyRuntimeHost host) { + _host = host; + } + + #region ITaggerProvider Members + + public ITagger CreateTagger(ITextBuffer buffer) where T : ITag { + return (ITagger)(buffer.GetOutliningTagger() ?? new OutliningTagger(buffer, _host)); + } + + #endregion + + internal class OutliningTagger : ITagger { + private readonly ITextBuffer _buffer; + private bool _enabled; + + public OutliningTagger(ITextBuffer buffer, IRubyRuntimeHost host) { + _buffer = buffer; + _buffer.Properties[typeof(OutliningTagger)] = this; + _enabled = host.EnterOutliningModeOnOpen; + } + + public bool Enabled { + get { + return _enabled; + } + } + + public void Enable() { + _enabled = true; + var snapshot = _buffer.CurrentSnapshot; + var tagsChanged = TagsChanged; + if (tagsChanged != null) { + tagsChanged(this, new SnapshotSpanEventArgs(new SnapshotSpan(snapshot, new Span(0, snapshot.Length)))); + } + } + + public void Disable() { + _enabled = false; + var snapshot = _buffer.CurrentSnapshot; + var tagsChanged = TagsChanged; + if (tagsChanged != null) { + tagsChanged(this, new SnapshotSpanEventArgs(new SnapshotSpan(snapshot, new Span(0, snapshot.Length)))); + } + } + + #region ITagger Members + + public IEnumerable> GetTags(NormalizedSnapshotSpanCollection spans) { + return new ITagSpan[0]; + } + + public event EventHandler TagsChanged; + + #endregion + } + + class TagSpan : ITagSpan { + private readonly SnapshotSpan _span; + private readonly OutliningTag _tag; + + public TagSpan(SnapshotSpan span, OutliningTag tag) { + _span = span; + _tag = tag; + } + + #region ITagSpan Members + + public SnapshotSpan Span { + get { return _span; } + } + + public IOutliningRegionTag Tag { + get { return _tag; } + } + + #endregion + } + + class OutliningTag : IOutliningRegionTag { + private readonly ITextSnapshot _snapshot; + private readonly Span _span; + private readonly bool _isImplementation; + + public OutliningTag(ITextSnapshot iTextSnapshot, Span span, bool isImplementation) { + _snapshot = iTextSnapshot; + _span = span; + _isImplementation = isImplementation; + } + + #region IOutliningRegionTag Members + + public object CollapsedForm { + get { return "..."; } + } + + public object CollapsedHintForm { + get { + string collapsedHint = _snapshot.GetText(_span); + + string[] lines = collapsedHint.Split(new string[] { "\r\n" }, StringSplitOptions.None); + // remove any leading white space for the preview + if (lines.Length > 0) { + int smallestWhiteSpace = Int32.MaxValue; + for (int i = 0; i < lines.Length; i++) { + string curLine = lines[i]; + + for (int j = 0; j < curLine.Length; j++) { + if (curLine[j] != ' ') { + smallestWhiteSpace = Math.Min(j, smallestWhiteSpace); + } + } + } + + for (int i = 0; i < lines.Length; i++) { + if (lines[i].Length >= smallestWhiteSpace) { + lines[i] = lines[i].Substring(smallestWhiteSpace); + } + } + + return String.Join("\r\n", lines); + } + return collapsedHint; + } + } + + public bool IsDefaultCollapsed { + get { return false; } + } + + public bool IsImplementation { + get { return _isImplementation; } + } + + #endregion + } + } + + static class OutliningTaggerProviderExtensions { + public static OutliningTaggerProvider.OutliningTagger GetOutliningTagger(this ITextView self) { + return self.TextBuffer.GetOutliningTagger(); + } + + public static OutliningTaggerProvider.OutliningTagger GetOutliningTagger(this ITextBuffer self) { + OutliningTaggerProvider.OutliningTagger res; + if (self.Properties.TryGetProperty(typeof(OutliningTaggerProvider.OutliningTagger), out res)) { + return res; + } + return null; + } + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Repl/RemoteRubyEvaluator.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Repl/RemoteRubyEvaluator.cs new file mode 100644 index 0000000000..8986e41a7f --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Repl/RemoteRubyEvaluator.cs @@ -0,0 +1,175 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Reflection; +using System.Runtime.Remoting; +using System.Threading; +using IronRuby; +using Microsoft.IronStudio.RemoteEvaluation; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Utils; + +namespace Microsoft.IronRubyTools.Library.Repl { + public class RemoteRubyEvaluator : RubyEvaluator { + private RemoteScriptFactory _factory; + private string _currentScopeName; + + static RemoteRubyEvaluator() { + AppDomain.CurrentDomain.AssemblyResolve += new ResolveEventHandler(CurrentDomain_AssemblyResolve); + } + + // Constructed via reflection when deserialized from the registry. + public RemoteRubyEvaluator() { + _factory = CreateFactory(); + } + + public static RemoteScriptFactory CreateFactory() { + return new RemoteScriptFactory(ApartmentState.STA); + } + + public RemoteScriptFactory RemoteScriptFactory { + get { + return _factory; + } + } + + public override void PublishScopeVariables(ScriptScope scope) { + } + + public override void Start() { + _currentScopeName = ""; + base.Start(); + } + + public override void Restart() { + WriteLine("Remote process has exited, restarting..."); + _factory = CreateFactory(); + Start(); + + var changed = AvailableScopesChanged; + if (changed != null) { + changed(this, EventArgs.Empty); + } + } + + public override void Reset() { + // TODO: strange text buffer behavior (race condition?) + // WriteLine("Remote process has been reset..."); + _factory.Shutdown(); + + _factory = CreateFactory(); + Start(); + + var changed = AvailableScopesChanged; + if (changed != null) { + changed(this, EventArgs.Empty); + } + } + + public override bool ExecuteText(string text, Action completionFunction) { + if (_factory.IsDisconnected) { + Restart(); + } + + return base.ExecuteText(text, completionFunction); + } + + public override string FormatException(ObjectHandle exception) { + if (_factory.IsDisconnected) { + Restart(); + } + + return base.FormatException(exception); + } + + public override bool CanExecuteText(string text) { + if (_factory.IsDisconnected) { + Restart(); + } + + return base.CanExecuteText(text); + } + + static Assembly CurrentDomain_AssemblyResolve(object sender, ResolveEventArgs args) { + // VS Loads us into the LoadFrom context, remoting needs to get the same assemblies + // but uses Assembly.Load. We return the correct assemblies here. + if (args.Name == typeof(ScriptRuntime).Assembly.FullName) { + return typeof(ScriptRuntime).Assembly; + } else if (args.Name == typeof(Ruby).Assembly.FullName) { + return typeof(Ruby).Assembly; + } + return null; + } + + protected override ScriptRuntime/*!*/ CreateRuntime() { + var setup = new ScriptRuntimeSetup(); + setup.AddRubySetup(); + return _factory.CreateRuntime(setup); + } + + protected override void RedirectIO(Stream/*!*/ stream, TextWriter/*!*/ writer, TextReader/*!*/ reader) { + _factory.SetConsoleOut(writer); + _factory.SetConsoleError(writer); + _factory.SetConsoleIn(reader); + } + + public override void AbortCommand() { + ThreadPool.QueueUserWorkItem(x => _factory.Abort()); + } + + + public event EventHandler AvailableScopesChanged; + + public IEnumerable GetAvailableScopes() { + return ArrayUtils.EmptyStrings; + } + + public override ICollection GetSignatureDocumentation(string expression) { + if (_factory.IsDisconnected) { + Restart(); + } + + return base.GetSignatureDocumentation(expression); + } + + public override ICollection GetMemberNames(string expression) { + if (_factory.IsDisconnected) { + Restart(); + } + + return base.GetMemberNames(expression); + } + + public override bool EnableMultipleScopes { + get { + return false; + } + } + + public string CurrentScopeName { + get { + return _currentScopeName; + } + } + + public ScriptScope CurrentScope { + get { + return _currentScope; + } + } + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Repl/RubyEvaluator.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Repl/RubyEvaluator.cs new file mode 100644 index 0000000000..e44f70d060 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/Repl/RubyEvaluator.cs @@ -0,0 +1,108 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.IronStudio.Library.Repl; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Runtime; +using IronRuby; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronRubyTools.Library.Repl { + public class RubyEvaluator : DlrEvaluator { + // Constructed via reflection when deserialized from the registry. + public RubyEvaluator() + : base("Ruby") { + } + + public override string/*!*/ Prompt { + get { return "»"; } + } + + public override string/*!*/ CommandPrefix { + get { return "."; } + } + + public override bool DisplayPromptInMargin { + get { return true; } + } + + public override void Start() { + base.Start(); + + InitScope(MakeScope()); + InitThread(); + } + + public virtual void PublishScopeVariables(ScriptScope scope) { + } + + protected override void InitScope(ScriptScope scope) { + base.InitScope(scope); + PublishScopeVariables(scope); + } + + public override void Reset() { + } + + protected virtual ScriptRuntime/*!*/ CreateRuntime() { + ScriptRuntimeSetup setup = new ScriptRuntimeSetup(); + setup.AddRubySetup(); + setup.DebugMode = true; + return Ruby.CreateRuntime(setup); + } + + protected virtual void RedirectIO(Stream/*!*/ stream, TextWriter/*!*/ writer, TextReader/*!*/ reader) { + // nop + } + + protected sealed override ScriptEngine/*!*/ MakeEngine(Stream/*!*/ stream, TextWriter/*!*/ writer, TextReader/*!*/ reader) { + var runtime = CreateRuntime(); + + runtime.IO.SetOutput(stream, writer); + RedirectIO(stream, writer, reader); + + runtime.LoadAssembly(typeof(string).Assembly); // mscorlib.dll + runtime.LoadAssembly(typeof(System.Uri).Assembly); // System.dll + + return runtime.GetRubyEngine(); + } + + protected virtual ScriptScope MakeScope() { + return _engine.CreateScope(); + } + + protected override SourceCodeKind SourceCodeKind { + get { + return SourceCodeKind.InteractiveCode; + } + } + + protected override ScriptScope ScopeForLastResult { + get { + throw new NotImplementedException("TODO"); + } + } + + public override bool EnableMultipleScopes { + get { + return false; + } + } + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/RubyClassifierProvider.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/RubyClassifierProvider.cs new file mode 100644 index 0000000000..13535deb52 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/RubyClassifierProvider.cs @@ -0,0 +1,61 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel.Composition; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Core; +using Microsoft.IronStudio.Library; +using Microsoft.Scripting.Hosting; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronRubyTools { + /// + /// Ruby classifier provider - we just subclass the DLR classifier provider and + /// give it our engine and content type. + /// + [Export(typeof(IClassifierProvider)), ContentType(RubyCoreConstants.ContentType)] + public class RubyClassifierProvider : DlrClassifierProvider { + private readonly IContentType _type; + private readonly ScriptEngine _engine; + public static RubyClassifierProvider Instance; + + [ImportingConstructor] + public RubyClassifierProvider(IRubyRuntimeHost host) + : this(host.ContentType, host.RubyScriptEngine) { + Instance = this; + } + + public RubyClassifierProvider(IContentType contentType, ScriptEngine scriptEngine) { + _type = contentType; + _engine = scriptEngine; + } + + #region IDlrClassifierProvider + + public override IContentType ContentType { + get { + return _type; + } + } + + public override ScriptEngine Engine { + get { + return _engine; + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/RubyCoreConstants.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/RubyCoreConstants.cs new file mode 100644 index 0000000000..171abe78d4 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/RubyCoreConstants.cs @@ -0,0 +1,26 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel.Composition; +using Microsoft.IronStudio; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronRubyTools { + internal static class RubyCoreConstants { + public const string ContentType = "IronRuby"; + + [Export, Name(ContentType), BaseDefinition(CoreConstants.DlrContentTypeName)] + internal static ContentTypeDefinition ContentTypeDefinition = null; + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/RubyRuntimeHost.cs b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/RubyRuntimeHost.cs new file mode 100644 index 0000000000..b43c3be5a1 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/IronRubyToolsCore/RubyRuntimeHost.cs @@ -0,0 +1,57 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironruby@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel.Composition; +using IronRuby; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Core; +using Microsoft.Scripting.Hosting; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronRubyTools { + [Export(typeof(IRubyRuntimeHost))] + internal sealed class RubyRuntimeHost : IRubyRuntimeHost { + private readonly IContentType/*!*/ _contentType; + private readonly ScriptEngine/*!*/ _engine; + private bool _enterOutliningOnOpen; + + [ImportingConstructor] + internal RubyRuntimeHost(IContentTypeRegistryService/*!*/ contentTypeRegistryService, IFileExtensionRegistryService/*!*/ fileExtensionRegistryService) { + _engine = Ruby.CreateEngine((setup) => { setup.Options["NoAssemblyResolveHook"] = true; }); + _contentType = contentTypeRegistryService.GetContentType(RubyCoreConstants.ContentType); + CoreUtils.RegisterExtensions(contentTypeRegistryService, fileExtensionRegistryService, _contentType, _engine.Setup.FileExtensions); + } + + public ScriptEngine RubyScriptEngine { + get { + return _engine; + } + } + + public IContentType ContentType { + get { + return _contentType; + } + } + + public bool EnterOutliningModeOnOpen { + get { + return _enterOutliningOnOpen; + } + set { + _enterOutliningOnOpen = value; + } + } + } +} diff --git a/Tools/IronStudio/IronRubyToolsCore/Properties/AssemblyInfo.cs b/Tools/IronStudio/IronRubyToolsCore/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..c75d122621 --- /dev/null +++ b/Tools/IronStudio/IronRubyToolsCore/Properties/AssemblyInfo.cs @@ -0,0 +1,42 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("IronRubyToolsCore")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("IronRubyToolsCore")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("D9C2D9EF-64F7-4445-92F4-CCA4B625AC56")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +#if MS_SIGNED +[assembly: InternalsVisibleTo("IronRubyTools, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +#else +[assembly: InternalsVisibleTo("IronRubyTools, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c10ce00dd2e0ce5046d68183d3ad035b47e92bf0ce7bcf8a03a217ca5d0b0c7db973fdf97579b52b502a23d4069dbf043389e1ab65a1d6c508a9837f3e2350f15e05cc63c0fc4b0410867a51919090e4c33f80203e9b0035b21c32bae20f98b068f90d99a50133a5336480d94039b176519f5fd8524765f33be43da65c4b68ba")] +#endif + diff --git a/Tools/IronStudio/IronStudio.vsmdi b/Tools/IronStudio/IronStudio.vsmdi new file mode 100644 index 0000000000..b317777fc9 --- /dev/null +++ b/Tools/IronStudio/IronStudio.vsmdi @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio.vssscc b/Tools/IronStudio/IronStudio.vssscc new file mode 100644 index 0000000000..794f014c92 --- /dev/null +++ b/Tools/IronStudio/IronStudio.vssscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROJECT" +} diff --git a/Tools/IronStudio/IronStudio/GlobalSuppressions.cs b/Tools/IronStudio/IronStudio/GlobalSuppressions.cs new file mode 100644 index 0000000000..85b00281af --- /dev/null +++ b/Tools/IronStudio/IronStudio/GlobalSuppressions.cs @@ -0,0 +1,25 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. Project-level +// suppressions either have no target or are given a specific target +// and scoped to a namespace, type, member, etc. +// +// To add a suppression to this file, right-click the message in the +// Error List, point to "Suppress Message(s)", and click "In Project +// Suppression File". You do not need to add suppressions to this +// file manually. + +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1017:MarkAssembliesWithComVisible")] diff --git a/Tools/IronStudio/IronStudio/Guids.cs b/Tools/IronStudio/IronStudio/Guids.cs new file mode 100644 index 0000000000..205224910c --- /dev/null +++ b/Tools/IronStudio/IronStudio/Guids.cs @@ -0,0 +1,28 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +// Guids.cs +// MUST match guids.h +using System; + +namespace Microsoft.IronStudio +{ + static class GuidList + { + public const string guidIronStudioPkgString = "F2D788A4-C316-4623-AE3A-FBE7E3E59E1A"; + public const string guidIronStudioCmdSetString = "FF3248AC-CB02-4CB4-A711-DD497D10D418"; + + public static readonly Guid guidIronStudioCmdSet = new Guid(guidIronStudioCmdSetString); + }; +} \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/IronStudio.Custom.pkgdef b/Tools/IronStudio/IronStudio/IronStudio.Custom.pkgdef new file mode 100644 index 0000000000..4580a509da Binary files /dev/null and b/Tools/IronStudio/IronStudio/IronStudio.Custom.pkgdef differ diff --git a/Tools/IronStudio/IronStudio/IronStudio.csproj b/Tools/IronStudio/IronStudio/IronStudio.csproj new file mode 100644 index 0000000000..996df340d1 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio.csproj @@ -0,0 +1,381 @@ + + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {C98DE16D-417E-4079-8939-1F8C8E28E2C8} + Library + Properties + Microsoft + IronStudio + v4.0 + 512 + SAK + SAK + SAK + SAK + + + true + full + false + $(SolutionDir)..\bin\Debug + DEBUG;TRACE;$(SignedSym) + prompt + 4 + 3021 + + + pdbonly + true + $(SolutionDir)..\bin\Release + TRACE;$(SignedSym) + prompt + 4 + + + + {7D07B0CE-FFA3-4402-BFF2-5F42B4267D2A} + Chiron + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Dynamic + + + {02FF0909-F5AD-48CF-A86A-345E721B7E40} + Microsoft.Scripting + + + {843716AE-38B3-4723-963C-950DD06BC4B8} + IronStudioCore + + + {D10C905C-7F15-41DF-9FF9-CCE461F571FD} + RemoteScriptFactory + False + + BuiltProjectOutputGroup + + + DebugSymbolsProjectOutputGroup + + + + + + True + + + False + + + False + + + False + + + + + + + + + + + + False + + + + + + + global + + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Shell.Immutable.10.0.dll + + + + + + + + + + + + + False + + + False + C:\Program Files (x86)\Microsoft Visual Studio 10.0\Common7\IDE\PrivateAssemblies\Microsoft.Windows.Design.Host.dll + + + + + + + + + + + + + + + True + + + True + + + True + + + False + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Form + + + ProgressForm.cs + + + + + + Resources.resx + True + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + true + . + PreserveNewest + + + + + + + Resources.cs + Designer + + + ProgressForm.cs + + + ResXFileCodeGenerator + Resources.Designer.cs + Designer + true + Microsoft.IronStudio + + + Designer + Microsoft.IronStudio + + + VisualStudio.Project.cs + + + Designer + Microsoft.IronStudio + + + + + + + + + + + + + + Designer + + + 1000 + Designer + + + + + true + true + + + + + diff --git a/Tools/IronStudio/IronStudio/IronStudio.csproj.vspscc b/Tools/IronStudio/IronStudio/IronStudio.csproj.vspscc new file mode 100644 index 0000000000..feffdecaa4 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/Tools/IronStudio/IronStudio/IronStudio.vsct b/Tools/IronStudio/IronStudio/IronStudio.vsct new file mode 100644 index 0000000000..8826308fdc --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio.vsct @@ -0,0 +1,268 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Interactive Console + Interactive Console + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/Tools/IronStudio/IronStudio/IronStudio/CommonConstants.cs b/Tools/IronStudio/IronStudio/IronStudio/CommonConstants.cs new file mode 100644 index 0000000000..637748d583 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/CommonConstants.cs @@ -0,0 +1,112 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants; + +namespace Microsoft.IronStudio { + public static class CommonConstants { + /// + /// . + /// + public const string UIContextNoSolution = "ADFC4E64-0397-11D1-9F4E-00A0C911004F"; + + /// + /// . + /// + public const string UIContextSolutionExists = "f1536ef8-92ec-443c-9ed7-fdadf150da82"; + + /// + /// Do not change this constant. It is a guid of the Visual Studio "Recent" page in + /// the "Add Reference" dialog. + /// + internal const string AddReferenceMRUPageGuid = "{19B97F03-9594-4c1c-BE28-25FF030113B3}"; + + /// + /// Do not change this constant. This is Visual Studio core text editor GUID. + /// + public static readonly Guid CMDUIGUID_TextEditor = new Guid("{8B382828-6202-11d1-8870-0000F87579D2}"); + + internal const string LibraryGuid = "c0000061-2c33-4277-bf44-7e5f2677d6b8"; + internal const string FileNodePropertiesGuid = "c000008b-a973-4d60-9518-5ea1411ccd67"; + internal const string SearchPathsPropertiesGuid = "D94E2410-B416-4CB2-B892-AE83D7BF7356"; + internal const string FolderNodePropertiesGuid = "c0000081-fb55-4d5d-901b-ee624db34961"; + internal const string ProjectNodePropertiesGuid = "c0000016-9ab0-4d58-80e6-54f29e8d3144"; + + //"Set As StartUp File" command + public const string StartupFile = "StartupFile"; + public const uint SetAsStartupFileCmdId = 0x3001; + + // Constants to handle the scrollbars. + public const int ConsoleHorizontalScrollbar = 0; + public const int ConsoleVerticalScrollbar = 1; + + //Search Paths container node context menu + public const int AddSearchPathCommandId = 0x4002; + + //Start Without Debugging command + public const int StartWithoutDebuggingCmdId = 0x4004; + + //Start Debugging command + public const int StartDebuggingCmdId = 0x4005; + + //Search Path project property + public const string SearchPath = "SearchPath"; + + //Interpreter Path project property + public const string InterpreterPath = "InterpreterPath"; + + //Working Directory project property + public const string WorkingDirectory = "WorkingDirectory"; + + //"Open Folder in Windows Explorer" command ID. + //Don't change this! This is Visual Studio constant. + public const int OpenFolderInExplorerCmdId = 1635; + + //Sort priority for the Working Directory node + //We want this node to be the first node in the Search Path subtree + public const int WorkingDirectorySortPriority = 100; + + //Sort priority for the Search Path container node + //We want this node to come after References, but before project home folders + public const int SearchPathContainerNodeSortPriority = 400; + + //Maximal sort priority for Search Path nodes + public const int SearchPathNodeMaxSortPriority = 110; + + //Project Home project property + public const string ProjectHome = "ProjectHome"; + + //TODO: Is there a constant in the SDK for this? + public const int SizeOfVariant = 16; + + //"Command line arguments" project property + public const string CommandLineArguments = "CommandLineArguments"; + + public const string IsWindowsApplication = "IsWindowsApplication"; + + //These are VS internal constants - don't change them + public static Guid Std97CmdGroupGuid = typeof(VSConstants.VSStd97CmdID).GUID; + public static Guid Std2KCmdGroupGuid = typeof(VSConstants.VSStd2KCmdID).GUID; + + //Command statuses + public const int NotSupportedInvisibleCmdStatus = (int)OleConstants.OLECMDERR_E_NOTSUPPORTED | + (int)OleConstants.OLECMDSTATE_INVISIBLE; + public const int SupportedEnabledCmdStatus = (int)(OLECMDF.OLECMDF_SUPPORTED | + OLECMDF.OLECMDF_ENABLED); + public const int SupportedCmdStatus = (int)OLECMDF.OLECMDF_SUPPORTED; + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/CommonPackage.cs b/Tools/IronStudio/IronStudio/IronStudio/CommonPackage.cs new file mode 100644 index 0000000000..98c9171f61 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/CommonPackage.cs @@ -0,0 +1,223 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel.Design; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.IronStudio.Navigation; +using Microsoft.Scripting.Hosting; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.TextManager.Interop; +using Microsoft.IronStudio.Project; + +namespace Microsoft.IronStudio { + public abstract class CommonPackage : Package, IOleComponent { + private uint _componentID; + private LibraryManager _libraryManager; + private ScriptEngine/*!*/ _engine; + + public ScriptEngine/*!*/ Engine { + get { + if (_engine == null) { + _engine = CreateScriptEngine(); + } + + return _engine; + } + } + + #region Language-specific abstracts + + public abstract ScriptEngine CreateScriptEngine(); + public abstract Type GetLibraryManagerType(); + public abstract LibraryManager CreateLibraryManager(CommonPackage package); + + // TODO: + // public abstract bool TryGetStartupFileAndDirectory(out string filename, out string dir); + + #endregion + + public CommonPackage() { + IServiceContainer container = this as IServiceContainer; + ServiceCreatorCallback callback = new ServiceCreatorCallback(CreateService); + //container.AddService(GetLanguageServiceType(), callback, true); + container.AddService(GetLibraryManagerType(), callback, true); + } + + private void RegisterForIdleTime() { + IOleComponentManager mgr = GetService(typeof(SOleComponentManager)) as IOleComponentManager; + if (_componentID == 0 && mgr != null) { + OLECRINFO[] crinfo = new OLECRINFO[1]; + crinfo[0].cbSize = (uint)Marshal.SizeOf(typeof(OLECRINFO)); + crinfo[0].grfcrf = (uint)_OLECRF.olecrfNeedIdleTime | + (uint)_OLECRF.olecrfNeedPeriodicIdleTime; + crinfo[0].grfcadvf = (uint)_OLECADVF.olecadvfModal | + (uint)_OLECADVF.olecadvfRedrawOff | + (uint)_OLECADVF.olecadvfWarningsOff; + crinfo[0].uIdleTimeInterval = 1000; + int hr = mgr.FRegisterComponent(this, crinfo, out _componentID); + } + } + + protected override void Dispose(bool disposing) { + try { + if (_componentID != 0) { + IOleComponentManager mgr = GetService(typeof(SOleComponentManager)) as IOleComponentManager; + if (mgr != null) { + mgr.FRevokeComponent(_componentID); + } + _componentID = 0; + } + if (null != _libraryManager) { + _libraryManager.Dispose(); + _libraryManager = null; + } + } finally { + base.Dispose(disposing); + } + } + + private object CreateService(IServiceContainer container, Type serviceType) { + if (GetLibraryManagerType() == serviceType) { + return _libraryManager = CreateLibraryManager(this); + } + return null; + } + + /// + /// Gets the current IWpfTextView that is the active document. + /// + /// + public static IWpfTextView GetActiveTextView() { + var monitorSelection = (IVsMonitorSelection)Package.GetGlobalService(typeof(SVsShellMonitorSelection)); + object curDocument; + if (ErrorHandler.Failed(monitorSelection.GetCurrentElementValue((uint)VSConstants.VSSELELEMID.SEID_DocumentFrame, out curDocument))) { + // TODO: Report error + return null; + } + + IVsWindowFrame frame = curDocument as IVsWindowFrame; + if (frame == null) { + // TODO: Report error + return null; + } + + object docView = null; + if (ErrorHandler.Failed(frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocView, out docView))) { + // TODO: Report error + return null; + } + + if (docView is IVsCodeWindow) { + IVsTextView textView; + if (ErrorHandler.Failed(((IVsCodeWindow)docView).GetPrimaryView(out textView))) { + // TODO: Report error + return null; + } + + var model = (IComponentModel)GetGlobalService(typeof(SComponentModel)); + var adapterFactory = model.GetService(); + var wpfTextView = adapterFactory.GetWpfTextView(textView); + return wpfTextView; + } + return null; + } + + public static IComponentModel ComponentModel { + get { + return (IComponentModel)GetGlobalService(typeof(SComponentModel)); + } + } + + public static bool TryGetStartupFileAndDirectory(out string filename, out string dir) { + var startupProject = GetStartupProject(); + if (startupProject != null) { + filename = startupProject.GetStartupFile(); + dir = startupProject.GetWorkingDirectory(); + } else { + var textView = CommonPackage.GetActiveTextView(); + if (textView == null) { + filename = null; + dir = null; + return false; + } + filename = textView.GetFilePath(); + dir = Path.GetDirectoryName(filename); + } + return true; + } + + internal static CommonProjectNode GetStartupProject() { + var buildMgr = (IVsSolutionBuildManager)Package.GetGlobalService(typeof(IVsSolutionBuildManager)); + IVsHierarchy hierarchy; + if (ErrorHandler.Succeeded(buildMgr.get_StartupProject(out hierarchy)) && hierarchy != null) { + return hierarchy.GetProject().GetCommonProject(); + } + return null; + } + + #region IOleComponent Members + + public int FContinueMessageLoop(uint uReason, IntPtr pvLoopData, MSG[] pMsgPeeked) { + return 1; + } + + public int FDoIdle(uint grfidlef) { + if (null != _libraryManager) { + _libraryManager.OnIdle(); + } + return 0; + } + + public int FPreTranslateMessage(MSG[] pMsg) { + return 0; + } + + public int FQueryTerminate(int fPromptUser) { + return 1; + } + + public int FReserved1(uint dwReserved, uint message, IntPtr wParam, IntPtr lParam) { + return 1; + } + + public IntPtr HwndGetWindow(uint dwWhich, uint dwReserved) { + return IntPtr.Zero; + } + + public void OnActivationChange(IOleComponent pic, int fSameComponent, OLECRINFO[] pcrinfo, int fHostIsActivating, OLECHOSTINFO[] pchostinfo, uint dwReserved) { + } + + public void OnAppActivate(int fActive, uint dwOtherThreadID) { + } + + public void OnEnterState(uint uStateID, int fEnter) { + } + + public void OnLoseActivation() { + } + + public void Terminate() { + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/CommonUtils.cs b/Tools/IronStudio/IronStudio/IronStudio/CommonUtils.cs new file mode 100644 index 0000000000..75c4c91dc6 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/CommonUtils.cs @@ -0,0 +1,121 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.IO; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio.Project.Automation; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TextManager.Interop; + +namespace Microsoft.IronStudio { + public static class CommonUtils { + private static readonly bool isProductionEnvironment = Environment.GetEnvironmentVariable("DLR_ROOT") == null; + /// + /// Ensures that the caller is only used for testing purposes + /// + public static void AssertIsTestOnlyCode() { + if (isProductionEnvironment) { + throw new InvalidOperationException("Test-only code called in production environment"); + } + } + + public static bool IsRecognizedFile(string filename, ScriptEngine engine) { + string extension = Path.GetExtension(filename); + foreach (string regext in engine.Setup.FileExtensions) { + if (string.Compare(extension, regext, StringComparison.OrdinalIgnoreCase) == 0) { + return true; + } + } + return false; + } + + /// + /// Returns absolute path of a given directory, always + /// ending with '/'. + /// + public static string NormalizeDirectoryPath(string path) { + string absPath = new Uri(path).LocalPath; + if (!absPath.EndsWith("\\")) { + absPath += "\\"; + } + return absPath; + } + + /// + /// Return true if both paths represent the same directory. + /// + public static bool AreTheSameDirectories(string path1, string path2) { + if (path1 == null || path2 == null) { + return false; + } + return NormalizeDirectoryPath(path1) == NormalizeDirectoryPath(path2); + } + + /// + /// Tries to create friendly directory path: '.' if the same as base path, + /// relative path if short, absolute path otherwise. + /// + public static string CreateFriendlyPath(string basePath, string path) { + string normalizedBaseDir = NormalizeDirectoryPath(basePath); + string normalizedDir = NormalizeDirectoryPath(path); + return normalizedBaseDir == normalizedDir ? " . " : + new DirectoryInfo(normalizedDir).Name; + } + + /// + /// Returns startup project in the currently open solution (if any). + /// + public static ProjectNode GetStartupProject(IServiceProvider provider) { + var solutionManager = (IVsSolutionBuildManager)provider.GetService( + typeof(IVsSolutionBuildManager)); + if (solutionManager == null) { + //Cannot get solution manager, assume no solution is open + return null; + } + IVsHierarchy startupPrj; + int hr = solutionManager.get_StartupProject(out startupPrj); + if (ErrorHandler.Failed(hr) || startupPrj == null) { + return null; + } + object project; + hr = startupPrj.GetProperty(VSConstants.VSITEMID_ROOT, + (int)__VSHPROPID.VSHPROPID_ExtObject, out project); + if (ErrorHandler.Failed(hr)) { + //Cannot get project object + return null; + } + OAProject oaProject = project as OAProject; + if (oaProject == null) { + //Unrecognized startup project type + return null; + } + return oaProject.Project; + } + + /// + /// Returns true is the solution is empty - used to detect the ad-hoc mode. + /// + public static bool IsSolutionEmpty() { + IVsSolution solution = (IVsSolution)Package.GetGlobalService(typeof(IVsSolution)); + object projectCount = 0; + solution.GetProperty((int)__VSPROPID.VSPROPID_ProjectCount, out projectCount); + return (int)projectCount == 0; + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/IronStudioPackage.cs b/Tools/IronStudio/IronStudio/IronStudio/IronStudioPackage.cs new file mode 100644 index 0000000000..0aab49282b --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/IronStudioPackage.cs @@ -0,0 +1,141 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.Globalization; +using System.Linq; +using System.Runtime.InteropServices; +using System.Threading; +using Microsoft.VisualStudio.Shell; +using Microsoft.IronStudio.Repl; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Utilities; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Text.Classification; +using System.ComponentModel; + +namespace Microsoft.IronStudio { + /// + /// This is the class that implements the package exposed by this assembly. + /// + /// The minimum requirement for a class to be considered a valid package for Visual Studio + /// is to implement the IVsPackage interface and register itself with the shell. + /// This package uses the helper classes defined inside the Managed Package Framework (MPF) + /// to do it: it derives from the Package class that provides the implementation of the + /// IVsPackage interface and uses the registration attributes defined in the framework to + /// register itself and its components with the shell. + /// + [PackageRegistration(UseManagedResourcesOnly = true)] // This attribute tells the PkgDef creation utility (CreatePkgDef.exe) that this class is a package. + [ProvideMenuResource(1000, 1)] // This attribute is needed to let the shell know that this package exposes some menus. + [ProvideKeyBindingTable(VsReplWindow.TypeGuid, 200)] // Resource ID: "Interactive Console" + [ProvideAutoLoad(CommonConstants.UIContextNoSolution)] + [ProvideAutoLoad(CommonConstants.UIContextSolutionExists)] + [ProvideToolWindow(typeof(VsReplWindow), MultiInstances = true)] + [Guid(GuidList.guidIronStudioPkgString)] // our packages GUID + public sealed class IronStudioPackage : Package, IVsToolWindowFactory { + public static IronStudioPackage Instance; + public IronStudioPackage() { + Instance = this; + } + + protected override void Initialize() { + base.Initialize(); + + // TODO: + //OleMenuCommandService mcs = GetService(typeof(IMenuCommandService)) as OleMenuCommandService; + //if (null != mcs) { + // CommandID menuCommandID = new CommandID(GuidList.guidIronStudioCmdSet, (int)PkgCmdIDList.cmdidBreakRepl); + // MenuCommand menuItem = new MenuCommand(Callback, menuCommandID); + // mcs.AddCommand(menuItem); + //} + } + + int IVsToolWindowFactory.CreateToolWindow(ref Guid toolWindowType, uint id) { + int num = (int)id; + if (toolWindowType == typeof(VsReplWindow).GUID) { + string evalAsm, eval, contentType, title; + Guid guid, langSvcGuid; + if (GetReplInfo(num, out evalAsm, out eval, out contentType, out title, out langSvcGuid, out guid)) { + + var model = (IComponentModel)GetService(typeof(SComponentModel)); + var contentTypes = model.GetService(); + var contentTypeObj = contentTypes.GetContentType(contentType); + var evaluator = (IReplEvaluator)Activator.CreateInstance(evalAsm, eval).Unwrap(); + + var replProvider = model.GetExtensions().First(); + replProvider.CreateReplWindow(evaluator, contentTypeObj, num, title, langSvcGuid, guid); + + return VSConstants.S_OK; + } + + return VSConstants.E_FAIL; + } + + foreach (Attribute attribute in Attribute.GetCustomAttributes(base.GetType())) { + if (attribute is ProvideToolWindowAttribute) { + ProvideToolWindowAttribute tool = (ProvideToolWindowAttribute)attribute; + if (tool.ToolType.GUID == toolWindowType) { + this.FindToolWindow(tool.ToolType, num, true); + break; + } + } + } + return 0; + } + + const string ActiveReplsKey = "ActiveRepls"; + + public void SaveReplInfo(int id, IReplEvaluator evaluator, IContentType contentType, string title, Guid languageServiceGuid, Guid replId) { + using (var repl = UserRegistryRoot.CreateSubKey(ActiveReplsKey)) { + using (var curRepl = repl.CreateSubKey(id.ToString())) { + curRepl.SetValue("EvaluatorType", evaluator.GetType().FullName); + curRepl.SetValue("EvaluatorAssembly", evaluator.GetType().Assembly.FullName); + curRepl.SetValue("ContentType", contentType.TypeName); + curRepl.SetValue("Title", title); + curRepl.SetValue("Guid", replId.ToString()); + curRepl.SetValue("LanguageServiceGuid", languageServiceGuid.ToString()); + } + } + } + + public bool GetReplInfo(int id, out string evaluator, out string evalType, out string contentType, out string title, out Guid languageServiceGuid, out Guid guid) { + using (var repl = UserRegistryRoot.OpenSubKey(ActiveReplsKey)) { + if (repl != null) { + using (var curRepl = repl.OpenSubKey(id.ToString())) { + if (curRepl != null) { + evaluator = (string)curRepl.GetValue("EvaluatorAssembly"); + evalType = (string)curRepl.GetValue("EvaluatorType"); + contentType = (string)curRepl.GetValue("ContentType"); + title = (string)curRepl.GetValue("Title"); + guid = Guid.Parse((string)curRepl.GetValue("Guid")); + languageServiceGuid = Guid.Parse((string)curRepl.GetValue("LanguageServiceGuid")); + return true; + } + } + } + } + evaluator = null; + contentType = null; + title = null; + evalType = null; + guid = Guid.Empty; + languageServiceGuid = Guid.Empty; + return false; + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/CommonLibraryNode.cs b/Tools/IronStudio/IronStudio/IronStudio/Navigation/CommonLibraryNode.cs new file mode 100644 index 0000000000..4fb4ac17bf --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/CommonLibraryNode.cs @@ -0,0 +1,210 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using Microsoft.Scripting; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TextManager.Interop; +using ErrorHandler = Microsoft.VisualStudio.ErrorHandler; +using VSConstants = Microsoft.VisualStudio.VSConstants; + +namespace Microsoft.IronStudio.Navigation { + + /// + /// This is a specialized version of the LibraryNode that handles the dynamic languages + /// items. The main difference from the generic one is that it supports navigation + /// to the location inside the source code where the element is defined. + /// + public abstract class CommonLibraryNode : LibraryNode { + private readonly IVsHierarchy _ownerHierarchy; + private readonly uint _fileId; + private readonly TextSpan _sourceSpan; + private readonly IScopeNode _scope; + private string _fileMoniker; + + protected CommonLibraryNode(IScopeNode scope, string namePrefix, IVsHierarchy hierarchy, uint itemId) : + base(GetLibraryNodeName(scope, namePrefix), GetLibraryNodeTypeFromScope(scope)) { + _ownerHierarchy = hierarchy; + _fileId = itemId; + + // Now check if we have all the information to navigate to the source location. + if ((null != _ownerHierarchy) && (VSConstants.VSITEMID_NIL != _fileId)) { + if ((SourceLocation.None != scope.Start) && (SourceLocation.None != scope.End)) { + _sourceSpan = new TextSpan(); + _sourceSpan.iStartIndex = scope.Start.Column - 1; + if (scope.Start.Line > 0) { + _sourceSpan.iStartLine = scope.Start.Line - 1; + } + _sourceSpan.iEndIndex = scope.End.Column; + if (scope.End.Line > 0) { + _sourceSpan.iEndLine = scope.End.Line - 1; + } + CanGoToSource = true; + } + } + _scope = scope; + } + + public IScopeNode ScopeNode { + get { + return _scope; + } + } + + private static string GetLibraryNodeName(IScopeNode node, string namePrefix) { + return node.IsFunction ? node.Name : string.Format(CultureInfo.InvariantCulture, "{0}{1}", namePrefix, node.Name); + } + + protected CommonLibraryNode(CommonLibraryNode node) : + base(node) { + _fileId = node._fileId; + _ownerHierarchy = node._ownerHierarchy; + _fileMoniker = node._fileMoniker; + } + + public override uint CategoryField(LIB_CATEGORY category) { + switch (category) { + case (LIB_CATEGORY)_LIB_CATEGORY2.LC_MEMBERINHERITANCE: + if (NodeType == LibraryNodeType.Members) { + return (uint)_LIBCAT_MEMBERINHERITANCE.LCMI_IMMEDIATE; + } + break; + } + return base.CategoryField(category); + } + + public override void GotoSource(VSOBJGOTOSRCTYPE gotoType) { + // We do not support the "Goto Reference" + if (VSOBJGOTOSRCTYPE.GS_REFERENCE == gotoType) { + return; + } + + // There is no difference between definition and declaration, so here we + // don't check for the other flags. + + IVsWindowFrame frame = null; + IntPtr documentData = FindDocDataFromRDT(); + try { + // Now we can try to open the editor. We assume that the owner hierarchy is + // a project and we want to use its OpenItem method. + IVsProject3 project = _ownerHierarchy as IVsProject3; + if (null == project) { + return; + } + Guid viewGuid = VSConstants.LOGVIEWID_Code; + ErrorHandler.ThrowOnFailure(project.OpenItem(_fileId, ref viewGuid, documentData, out frame)); + } finally { + if (IntPtr.Zero != documentData) { + Marshal.Release(documentData); + documentData = IntPtr.Zero; + } + } + + // Make sure that the document window is visible. + ErrorHandler.ThrowOnFailure(frame.Show()); + + // Get the code window from the window frame. + object docView; + ErrorHandler.ThrowOnFailure(frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocView, out docView)); + IVsCodeWindow codeWindow = docView as IVsCodeWindow; + if (null == codeWindow) { + object docData; + ErrorHandler.ThrowOnFailure(frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocData, out docData)); + codeWindow = docData as IVsCodeWindow; + if (null == codeWindow) { + return; + } + } + + // Get the primary view from the code window. + IVsTextView textView; + ErrorHandler.ThrowOnFailure(codeWindow.GetPrimaryView(out textView)); + + // Set the cursor at the beginning of the declaration. + ErrorHandler.ThrowOnFailure(textView.SetCaretPos(_sourceSpan.iStartLine, _sourceSpan.iStartIndex)); + // Make sure that the text is visible. + TextSpan visibleSpan = new TextSpan(); + visibleSpan.iStartLine = _sourceSpan.iStartLine; + visibleSpan.iStartIndex = _sourceSpan.iStartIndex; + visibleSpan.iEndLine = _sourceSpan.iStartLine; + visibleSpan.iEndIndex = _sourceSpan.iStartIndex + 1; + ErrorHandler.ThrowOnFailure(textView.EnsureSpanVisible(visibleSpan)); + + } + + public override void SourceItems(out IVsHierarchy hierarchy, out uint itemId, out uint itemsCount) { + hierarchy = _ownerHierarchy; + itemId = _fileId; + itemsCount = 1; + } + + public override string UniqueName { + get { + if (string.IsNullOrEmpty(_fileMoniker)) { + ErrorHandler.ThrowOnFailure(_ownerHierarchy.GetCanonicalName(_fileId, out _fileMoniker)); + } + return string.Format(CultureInfo.InvariantCulture, "{0}/{1}", _fileMoniker, Name); + } + } + + private IntPtr FindDocDataFromRDT() { + // Get a reference to the RDT. + IVsRunningDocumentTable rdt = Package.GetGlobalService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + if (null == rdt) { + return IntPtr.Zero; + } + + // Get the enumeration of the running documents. + IEnumRunningDocuments documents; + ErrorHandler.ThrowOnFailure(rdt.GetRunningDocumentsEnum(out documents)); + + IntPtr documentData = IntPtr.Zero; + uint[] docCookie = new uint[1]; + uint fetched; + while ((VSConstants.S_OK == documents.Next(1, docCookie, out fetched)) && (1 == fetched)) { + uint flags; + uint editLocks; + uint readLocks; + string moniker; + IVsHierarchy docHierarchy; + uint docId; + IntPtr docData = IntPtr.Zero; + try { + ErrorHandler.ThrowOnFailure( + rdt.GetDocumentInfo(docCookie[0], out flags, out readLocks, out editLocks, out moniker, out docHierarchy, out docId, out docData)); + // Check if this document is the one we are looking for. + if ((docId == _fileId) && (_ownerHierarchy.Equals(docHierarchy))) { + documentData = docData; + docData = IntPtr.Zero; + break; + } + } finally { + if (IntPtr.Zero != docData) { + Marshal.Release(docData); + } + } + } + + return documentData; + } + + private static LibraryNodeType GetLibraryNodeTypeFromScope(IScopeNode scope) { + return scope.IsFunction ? LibraryNodeType.Members : LibraryNodeType.Classes; + } + + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/HierarchyListener.cs b/Tools/IronStudio/IronStudio/IronStudio/Navigation/HierarchyListener.cs new file mode 100644 index 0000000000..44052cf37f --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/HierarchyListener.cs @@ -0,0 +1,253 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Hosting; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TextManager.Interop; +using VSConstants = Microsoft.VisualStudio.VSConstants; + +namespace Microsoft.IronStudio.Navigation { + + public class HierarchyEventArgs : EventArgs { + private uint _itemId; + private string _fileName; + private IVsTextLines _buffer; + + public HierarchyEventArgs(uint itemId, string canonicalName) { + _itemId = itemId; + _fileName = canonicalName; + } + public string CanonicalName { + get { return _fileName; } + } + public uint ItemID { + get { return _itemId; } + } + public IVsTextLines TextBuffer { + get { return _buffer; } + set { _buffer = value; } + } + } + + public class HierarchyListener : IVsHierarchyEvents, IDisposable { + private IVsHierarchy _hierarchy; + private uint _cookie; + private ScriptEngine _engine; + + public HierarchyListener(IVsHierarchy hierarchy, ScriptEngine engine) { + if (null == hierarchy) { + throw new ArgumentNullException("hierarchy"); + } + if (null == engine) { + throw new ArgumentNullException("engine"); + } + _hierarchy = hierarchy; + _engine = engine; + } + + protected IVsHierarchy Hierarchy { + get { return _hierarchy; } + } + + #region Public Methods + public bool IsListening { + get { return (0 != _cookie); } + } + public void StartListening(bool doInitialScan) { + if (0 != _cookie) { + return; + } + ErrorHandler.ThrowOnFailure( + _hierarchy.AdviseHierarchyEvents(this, out _cookie)); + if (doInitialScan) { + InternalScanHierarchy(VSConstants.VSITEMID_ROOT); + } + } + public void StopListening() { + InternalStopListening(true); + } + #endregion + + #region IDisposable Members + + public void Dispose() { + InternalStopListening(false); + _cookie = 0; + _hierarchy = null; + } + + #endregion + + #region Public Events + private EventHandler onItemAdded; + public event EventHandler OnAddItem { + add { onItemAdded += value; } + remove { onItemAdded -= value; } + } + + private EventHandler onItemDeleted; + public event EventHandler OnDeleteItem { + add { onItemDeleted += value; } + remove { onItemDeleted -= value; } + } + + #endregion + + #region IVsHierarchyEvents Members + + public int OnInvalidateIcon(IntPtr hicon) { + // Do Nothing. + return VSConstants.S_OK; + } + + public int OnInvalidateItems(uint itemidParent) { + // TODO: Find out if this event is needed. + return VSConstants.S_OK; + } + + public int OnItemAdded(uint itemidParent, uint itemidSiblingPrev, uint itemidAdded) { + // Check if the item is my language file. + string name; + if (!IsAnalyzableSource(itemidAdded, out name)) { + return VSConstants.S_OK; + } + + // This item is a my language file, so we can notify that it is added to the hierarchy. + if (null != onItemAdded) { + HierarchyEventArgs args = new HierarchyEventArgs(itemidAdded, name); + onItemAdded(_hierarchy, args); + } + return VSConstants.S_OK; + } + + public int OnItemDeleted(uint itemid) { + // Notify that the item is deleted only if it is my language file. + string name; + if (!IsAnalyzableSource(itemid, out name)) { + return VSConstants.S_OK; + } + if (null != onItemDeleted) { + HierarchyEventArgs args = new HierarchyEventArgs(itemid, name); + onItemDeleted(_hierarchy, args); + } + return VSConstants.S_OK; + } + + public int OnItemsAppended(uint itemidParent) { + // TODO: Find out what this event is about. + return VSConstants.S_OK; + } + + public int OnPropertyChanged(uint itemid, int propid, uint flags) { + // Do Nothing. + return VSConstants.S_OK; + } + #endregion + + private bool InternalStopListening(bool throwOnError) { + if ((null != _hierarchy) || (0 == _cookie)) { + return false; + } + int hr = _hierarchy.UnadviseHierarchyEvents(_cookie); + if (throwOnError) { + ErrorHandler.ThrowOnFailure(hr); + } + _cookie = 0; + return ErrorHandler.Succeeded(hr); + } + + /// + /// Do a recursive walk on the hierarchy to find all this language files in it. + /// It will generate an event for every file found. + /// + private void InternalScanHierarchy(uint itemId) { + uint currentItem = itemId; + while (VSConstants.VSITEMID_NIL != currentItem) { + // If this item is a my language file, then send the add item event. + string itemName; + if ((null != onItemAdded) && IsAnalyzableSource(currentItem, out itemName)) { + HierarchyEventArgs args = new HierarchyEventArgs(currentItem, itemName); + onItemAdded(_hierarchy, args); + } + + // NOTE: At the moment we skip the nested hierarchies, so here we look for the + // children of this node. + // Before looking at the children we have to make sure that the enumeration has not + // side effects to avoid unexpected behavior. + object propertyValue; + bool canScanSubitems = true; + int hr = _hierarchy.GetProperty(currentItem, (int)__VSHPROPID.VSHPROPID_HasEnumerationSideEffects, out propertyValue); + if ((VSConstants.S_OK == hr) && (propertyValue is bool)) { + canScanSubitems = !(bool)propertyValue; + } + // If it is allow to look at the sub-items of the current one, lets do it. + if (canScanSubitems) { + object child; + hr = _hierarchy.GetProperty(currentItem, (int)__VSHPROPID.VSHPROPID_FirstChild, out child); + if (VSConstants.S_OK == hr) { + // There is a sub-item, call this same function on it. + InternalScanHierarchy(GetItemId(child)); + } + } + + // Move the current item to its first visible sibling. + object sibling; + hr = _hierarchy.GetProperty(currentItem, (int)__VSHPROPID.VSHPROPID_NextSibling, out sibling); + if (VSConstants.S_OK != hr) { + currentItem = VSConstants.VSITEMID_NIL; + } else { + currentItem = GetItemId(sibling); + } + } + } + + private bool IsAnalyzableSource(uint itemId, out string canonicalName) { + // Find out if this item is a physical file. + Guid typeGuid; + canonicalName = null; + int hr = Hierarchy.GetGuidProperty(itemId, (int)__VSHPROPID.VSHPROPID_TypeGuid, out typeGuid); + if (Microsoft.VisualStudio.ErrorHandler.Failed(hr) || + VSConstants.GUID_ItemType_PhysicalFile != typeGuid) { + // It is not a file, we can exit now. + return false; + } + + // This item is a file; find if current language can recognize it. + hr = Hierarchy.GetCanonicalName(itemId, out canonicalName); + if (ErrorHandler.Failed(hr)) { + return false; + } + return (System.IO.Path.GetExtension(canonicalName).Equals(".xaml", StringComparison.OrdinalIgnoreCase)) || + CommonUtils.IsRecognizedFile(canonicalName, _engine); + } + + /// + /// Gets the item id. + /// + /// VARIANT holding an itemid. + /// Item Id of the concerned node + private static uint GetItemId(object variantValue) + { + if (variantValue == null) return VSConstants.VSITEMID_NIL; + if (variantValue is int) return (uint)(int)variantValue; + if (variantValue is uint) return (uint)variantValue; + if (variantValue is short) return (uint)(short)variantValue; + if (variantValue is ushort) return (uint)(ushort)variantValue; + if (variantValue is long) return (uint)(long)variantValue; + return VSConstants.VSITEMID_NIL; + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/ICustomSearchListProvider.cs b/Tools/IronStudio/IronStudio/IronStudio/Navigation/ICustomSearchListProvider.cs new file mode 100644 index 0000000000..a320058a28 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/ICustomSearchListProvider.cs @@ -0,0 +1,21 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.IronStudio.Navigation { + interface ICustomSearchListProvider { + IVsSimpleObjectList2 GetSearchList(); + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/ILibraryManager.cs b/Tools/IronStudio/IronStudio/IronStudio/Navigation/ILibraryManager.cs new file mode 100644 index 0000000000..952c062663 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/ILibraryManager.cs @@ -0,0 +1,30 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TextManager.Interop; + +namespace Microsoft.IronStudio.Navigation { + /// + /// This interface defines the service that finds current language files inside a hierarchy + /// and builds information to expose to the class view or object browser. + /// + public interface ILibraryManager { + void RegisterHierarchy(IVsHierarchy hierarchy); + void UnregisterHierarchy(IVsHierarchy hierarchy); + void RegisterLineChangeHandler(uint document, TextLineChangeEvent lineChanged, Action onIdle); + } + public delegate void TextLineChangeEvent(object sender, TextLineChange[] changes, int last); +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/ISimpleObject.cs b/Tools/IronStudio/IronStudio/IronStudio/Navigation/ISimpleObject.cs new file mode 100644 index 0000000000..6dd745abde --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/ISimpleObject.cs @@ -0,0 +1,46 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel.Design; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.IronStudio.Navigation { + public interface ISimpleObject { + bool CanDelete { get; } + bool CanGoToSource { get; } + bool CanRename { get; } + string Name { get; } + string UniqueName { get; } + string GetTextRepresentation(VSTREETEXTOPTIONS options); + string TooltipText { get; } + object BrowseObject { get; } + CommandID ContextMenuID { get; } + VSTREEDISPLAYDATA DisplayData { get; } + + uint CategoryField(LIB_CATEGORY lIB_CATEGORY); + + void Delete(); + void DoDragDrop(OleDataObject dataObject, uint grfKeyState, uint pdwEffect); + void Rename(string pszNewName, uint grfFlags); + void GotoSource(VSOBJGOTOSRCTYPE SrcType); + + void SourceItems(out IVsHierarchy ppHier, out uint pItemid, out uint pcItems); + uint EnumClipboardFormats(_VSOBJCFFLAGS _VSOBJCFFLAGS, VSOBJCLIPFORMAT[] rgcfFormats); + void FillDescription(_VSOBJDESCOPTIONS _VSOBJDESCOPTIONS, IVsObjectBrowserDescription3 pobDesc); + + IVsSimpleObjectList2 FilterView(uint ListType); + } + +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/Library.cs b/Tools/IronStudio/IronStudio/IronStudio/Navigation/Library.cs new file mode 100644 index 0000000000..88f344a0c4 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/Library.cs @@ -0,0 +1,132 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell.Interop; +using VSConstants = Microsoft.VisualStudio.VSConstants; + +namespace Microsoft.IronStudio.Navigation { + + /// + /// Implements a simple library that tracks project symbols, objects etc. + /// + public class Library : IVsSimpleLibrary2 { + private Guid _guid; + private _LIB_FLAGS2 _capabilities; + private LibraryNode _root; + private uint _updateCount; + + public Library(Guid libraryGuid) { + _guid = libraryGuid; + _root = new LibraryNode(String.Empty, LibraryNode.LibraryNodeType.Package); + } + + public _LIB_FLAGS2 LibraryCapabilities { + get { return _capabilities; } + set { _capabilities = value; } + } + + internal void AddNode(LibraryNode node) { + lock (this) { + // re-create root node here because we may have handed out the node before and don't want to mutate it's list. + _root = new LibraryNode(_root); + _root.AddNode(node); + _updateCount++; + } + } + + internal void RemoveNode(LibraryNode node) { + lock (this) { + _root = new LibraryNode(_root); + _root.RemoveNode(node); + _updateCount++; + } + } + + #region IVsSimpleLibrary2 Members + + public int AddBrowseContainer(VSCOMPONENTSELECTORDATA[] pcdComponent, ref uint pgrfOptions, out string pbstrComponentAdded) { + pbstrComponentAdded = null; + return VSConstants.E_NOTIMPL; + } + + public int CreateNavInfo(SYMBOL_DESCRIPTION_NODE[] rgSymbolNodes, uint ulcNodes, out IVsNavInfo ppNavInfo) { + ppNavInfo = null; + return VSConstants.E_NOTIMPL; + } + + public int GetBrowseContainersForHierarchy(IVsHierarchy pHierarchy, uint celt, VSBROWSECONTAINER[] rgBrowseContainers, uint[] pcActual) { + return VSConstants.E_NOTIMPL; + } + + public int GetGuid(out Guid pguidLib) { + pguidLib = _guid; + return VSConstants.S_OK; + } + + public int GetLibFlags2(out uint pgrfFlags) { + pgrfFlags = (uint)LibraryCapabilities; + return VSConstants.S_OK; + } + + public int GetList2(uint ListType, uint flags, VSOBSEARCHCRITERIA2[] pobSrch, out IVsSimpleObjectList2 ppIVsSimpleObjectList2) { + ICustomSearchListProvider listProvider; + if(pobSrch != null && + pobSrch.Length > 0 && + (listProvider = pobSrch[0].pIVsNavInfo as ICustomSearchListProvider) != null) { + switch ((_LIB_LISTTYPE)ListType) { + case _LIB_LISTTYPE.LLT_NAMESPACES: + ppIVsSimpleObjectList2 = listProvider.GetSearchList(); + break; + default: + ppIVsSimpleObjectList2 = null; + return VSConstants.E_FAIL; + } + } else { + ppIVsSimpleObjectList2 = _root as IVsSimpleObjectList2; + } + return VSConstants.S_OK; + } + + public int GetSeparatorStringWithOwnership(out string pbstrSeparator) { + pbstrSeparator = "."; + return VSConstants.S_OK; + } + + public int GetSupportedCategoryFields2(int Category, out uint pgrfCatField) { + pgrfCatField = (uint)_LIB_CATEGORY2.LC_HIERARCHYTYPE | (uint)_LIB_CATEGORY2.LC_PHYSICALCONTAINERTYPE; + return VSConstants.S_OK; + } + + public int LoadState(IStream pIStream, LIB_PERSISTTYPE lptType) { + return VSConstants.S_OK; + } + + public int RemoveBrowseContainer(uint dwReserved, string pszLibName) { + return VSConstants.E_NOTIMPL; + } + + public int SaveState(IStream pIStream, LIB_PERSISTTYPE lptType) { + return VSConstants.S_OK; + } + + public int UpdateCounter(out uint pCurUpdate) { + pCurUpdate = _updateCount; + return VSConstants.S_OK; + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/LibraryManager.cs b/Tools/IronStudio/IronStudio/IronStudio/Navigation/LibraryManager.cs new file mode 100644 index 0000000000..070f044d3f --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/LibraryManager.cs @@ -0,0 +1,433 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Design; +using System.Runtime.InteropServices; +using Microsoft.Scripting.Utils; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.TextManager.Interop; + +namespace Microsoft.IronStudio.Navigation { + + /// + /// Inplementation of the service that builds the information to expose to the symbols + /// navigation tools (class view or object browser) from the source files inside a + /// hierarchy. + /// + public abstract class LibraryManager : IDisposable, IVsRunningDocTableEvents { + private readonly CommonPackage/*!*/ _package; + private readonly Dictionary _documents; + private readonly Dictionary _hierarchies; + private readonly Dictionary _files; + private readonly Library _library; + private readonly IVsEditorAdaptersFactoryService _adapterFactory; + private uint _objectManagerCookie; + private uint _runningDocTableCookie; + + public LibraryManager(CommonPackage/*!*/ package) { + ContractUtils.RequiresNotNull(package, "package"); + _package = package; + _documents = new Dictionary(); + _hierarchies = new Dictionary(); + _library = new Library(new Guid(CommonConstants.LibraryGuid)); + _library.LibraryCapabilities = (_LIB_FLAGS2)_LIB_FLAGS.LF_PROJECT; + _files = new Dictionary(); + + var model = ((IServiceContainer)package).GetService(typeof(SComponentModel)) as IComponentModel; + _adapterFactory = model.GetService(); + + // Register our library now so it'll be available for find all references + RegisterLibrary(); + } + + protected abstract LibraryNode CreateLibraryNode(IScopeNode subItem, string namePrefix, IVsHierarchy hierarchy, uint itemid); + + private object GetPackageService(Type/*!*/ type) { + return ((IServiceProvider)_package).GetService(type); + } + + private void RegisterForRDTEvents() { + if (0 != _runningDocTableCookie) { + return; + } + IVsRunningDocumentTable rdt = GetPackageService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + if (null != rdt) { + // Do not throw here in case of error, simply skip the registration. + rdt.AdviseRunningDocTableEvents(this, out _runningDocTableCookie); + } + } + + private void UnregisterRDTEvents() { + if (0 == _runningDocTableCookie) { + return; + } + IVsRunningDocumentTable rdt = GetPackageService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + if (null != rdt) { + // Do not throw in case of error. + rdt.UnadviseRunningDocTableEvents(_runningDocTableCookie); + } + _runningDocTableCookie = 0; + } + + #region ILibraryManager Members + + public void RegisterHierarchy(IVsHierarchy hierarchy) { + if ((null == hierarchy) || _hierarchies.ContainsKey(hierarchy)) { + return; + } + + RegisterLibrary(); + + HierarchyListener listener = new HierarchyListener(hierarchy, _package.Engine); + listener.OnAddItem += new EventHandler(OnNewFile); + listener.OnDeleteItem += new EventHandler(OnDeleteFile); + listener.StartListening(true); + _hierarchies.Add(hierarchy, listener); + RegisterForRDTEvents(); + } + + private void RegisterLibrary() { + if (0 == _objectManagerCookie) { + IVsObjectManager2 objManager = GetPackageService(typeof(SVsObjectManager)) as IVsObjectManager2; + if (null == objManager) { + return; + } + Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure( + objManager.RegisterSimpleLibrary(_library, out _objectManagerCookie)); + } + } + + public void UnregisterHierarchy(IVsHierarchy hierarchy) { + if ((null == hierarchy) || !_hierarchies.ContainsKey(hierarchy)) { + return; + } + HierarchyListener listener = _hierarchies[hierarchy]; + if (null != listener) { + listener.Dispose(); + } + _hierarchies.Remove(hierarchy); + if (0 == _hierarchies.Count) { + UnregisterRDTEvents(); + } + lock (_files) { + ModuleId[] keys = new ModuleId[_files.Keys.Count]; + _files.Keys.CopyTo(keys, 0); + foreach (ModuleId id in keys) { + if (hierarchy.Equals(id.Hierarchy)) { + _library.RemoveNode(_files[id]); + _files.Remove(id); + } + } + } + // Remove the document listeners. + uint[] docKeys = new uint[_documents.Keys.Count]; + _documents.Keys.CopyTo(docKeys, 0); + foreach (uint id in docKeys) { + TextLineEventListener docListener = _documents[id]; + if (hierarchy.Equals(docListener.FileID.Hierarchy)) { + _documents.Remove(id); + docListener.Dispose(); + } + } + } + + public void RegisterLineChangeHandler(uint document, + TextLineChangeEvent lineChanged, Action onIdle) { + _documents[document].OnFileChangedImmediate += delegate(object sender, TextLineChange[] changes, int fLast) { + lineChanged(sender, changes, fLast); + }; + _documents[document].OnFileChanged += delegate(object sender, HierarchyEventArgs args) { + onIdle(args.TextBuffer); + }; + } + + #endregion + + #region Library Member Production + + /// + /// Overridden in the base class to receive notifications of when a file should + /// be analyzed for inclusion in the library. The derived class should queue + /// the parsing of the file and when it's complete it should call FileParsed + /// with the provided LibraryTask and an IScopeNode which provides information + /// about the members of the file. + /// + protected virtual void OnNewFile(LibraryTask task) { + } + + /// + /// Called by derived class when a file has been parsed. The caller should + /// provide the LibraryTask received from the OnNewFile call and an IScopeNode + /// which represents the contents of the library. + /// + /// It is safe to call this method from any thread. + /// + protected void FileParsed(LibraryTask task, IScopeNode scope) { + LibraryNode module = new LibraryNode( + System.IO.Path.GetFileName(task.FileName), + LibraryNode.LibraryNodeType.PhysicalContainer + ); + + // TODO: Creating the module tree should be done lazily as needed + // Currently we replace the entire tree and rely upon the libraries + // update count to invalidate the whole thing. We could do this + // finer grained and only update the changed nodes. But then we + // need to make sure we're not mutating lists which are handed out. + CreateModuleTree(module, module, scope, "", task.ModuleID); + + if (null != task.ModuleID) { + LibraryNode previousItem = null; + lock (_files) { + if (_files.TryGetValue(task.ModuleID, out previousItem)) { + _files.Remove(task.ModuleID); + } + } + _library.RemoveNode(previousItem); + } + _library.AddNode(module); + if (null != task.ModuleID) { + lock (_files) { + _files.Add(task.ModuleID, module); + } + } + } + + private void CreateModuleTree(LibraryNode root, LibraryNode current, IScopeNode scope, string namePrefix, ModuleId moduleId) { + if ((null == root) || (null == scope) || (null == scope.NestedScopes)) { + return; + } + foreach (IScopeNode subItem in scope.NestedScopes) { + LibraryNode newNode = CreateLibraryNode(subItem, namePrefix, moduleId.Hierarchy, moduleId.ItemID); + string newNamePrefix = namePrefix; + + // The classes are always added to the root node, the functions to the + // current node. + if ((newNode.NodeType & LibraryNode.LibraryNodeType.Members) != LibraryNode.LibraryNodeType.None) { + current.AddNode(newNode); + } else if ((newNode.NodeType & LibraryNode.LibraryNodeType.Classes) != LibraryNode.LibraryNodeType.None) { + // Classes are always added to the root. + root.AddNode(newNode); + newNamePrefix = newNode.Name + "."; + } + + // Now use recursion to get the other types. + CreateModuleTree(root, newNode, subItem, newNamePrefix, moduleId); + } + } + #endregion + + #region Hierarchy Events + + private void OnNewFile(object sender, HierarchyEventArgs args) { + IVsHierarchy hierarchy = sender as IVsHierarchy; + if (null == hierarchy) { + return; + } + + ITextBuffer buffer = null; + if (null != args.TextBuffer) { + buffer = _adapterFactory.GetDocumentBuffer(args.TextBuffer); + } + + OnNewFile(new LibraryTask(args.CanonicalName, buffer, new ModuleId(hierarchy, args.ItemID))); + } + + private void OnDeleteFile(object sender, HierarchyEventArgs args) { + IVsHierarchy hierarchy = sender as IVsHierarchy; + if (null == hierarchy) { + return; + } + + ModuleId id = new ModuleId(hierarchy, args.ItemID); + LibraryNode node = null; + lock (_files) { + if (_files.TryGetValue(id, out node)) { + _files.Remove(id); + } + } + if (null != node) { + _library.RemoveNode(node); + } + } + + /// + /// Checks whether this hierarchy item is a project member (files in search path aren't + /// considered as project members). + /// + private bool IsProjectMember(IVsHierarchy hierarchy, uint itemId) { + object val; + int hr = hierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_IsNonMemberItem, out val); + return ErrorHandler.Succeeded(hr) && !(bool)val; + } + + #endregion + + #region IVsRunningDocTableEvents Members + + public int OnAfterAttributeChange(uint docCookie, uint grfAttribs) { + if ((grfAttribs & (uint)(__VSRDTATTRIB.RDTA_MkDocument)) == (uint)__VSRDTATTRIB.RDTA_MkDocument) { + IVsRunningDocumentTable rdt = GetPackageService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + if (rdt != null) { + uint flags, readLocks, editLocks, itemid; + IVsHierarchy hier; + IntPtr docData = IntPtr.Zero; + string moniker; + int hr; + try { + hr = rdt.GetDocumentInfo(docCookie, out flags, out readLocks, out editLocks, out moniker, out hier, out itemid, out docData); + TextLineEventListener listner; + if (_documents.TryGetValue(docCookie, out listner)) { + listner.FileName = moniker; + } + } finally { + if (IntPtr.Zero != docData) { + Marshal.Release(docData); + } + } + } + } + return VSConstants.S_OK; + } + + public int OnAfterDocumentWindowHide(uint docCookie, IVsWindowFrame pFrame) { + return VSConstants.S_OK; + } + + public int OnAfterFirstDocumentLock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) { + return VSConstants.S_OK; + } + + public int OnAfterSave(uint docCookie) { + return VSConstants.S_OK; + } + + public int OnBeforeDocumentWindowShow(uint docCookie, int fFirstShow, IVsWindowFrame pFrame) { + // Check if this document is in the list of the documents. + if (_documents.ContainsKey(docCookie)) { + return VSConstants.S_OK; + } + // Get the information about this document from the RDT. + IVsRunningDocumentTable rdt = GetPackageService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + if (null != rdt) { + // Note that here we don't want to throw in case of error. + uint flags; + uint readLocks; + uint writeLoks; + string documentMoniker; + IVsHierarchy hierarchy; + uint itemId; + IntPtr unkDocData; + int hr = rdt.GetDocumentInfo(docCookie, out flags, out readLocks, out writeLoks, + out documentMoniker, out hierarchy, out itemId, out unkDocData); + try { + if (Microsoft.VisualStudio.ErrorHandler.Failed(hr) || (IntPtr.Zero == unkDocData)) { + return VSConstants.S_OK; + } + // Check if the herarchy is one of the hierarchies this service is monitoring. + if (!_hierarchies.ContainsKey(hierarchy)) { + // This hierarchy is not monitored, we can exit now. + return VSConstants.S_OK; + } + + // Check the file to see if a listener is required. + if (CommonUtils.IsRecognizedFile(documentMoniker, _package.Engine)) { + return VSConstants.S_OK; + } + + // Create the module id for this document. + ModuleId docId = new ModuleId(hierarchy, itemId); + + // Try to get the text buffer. + IVsTextLines buffer = Marshal.GetObjectForIUnknown(unkDocData) as IVsTextLines; + + // Create the listener. + TextLineEventListener listener = new TextLineEventListener(buffer, documentMoniker, docId); + // Set the event handler for the change event. Note that there is no difference + // between the AddFile and FileChanged operation, so we can use the same handler. + listener.OnFileChanged += new EventHandler(OnNewFile); + // Add the listener to the dictionary, so we will not create it anymore. + _documents.Add(docCookie, listener); + } finally { + if (IntPtr.Zero != unkDocData) { + Marshal.Release(unkDocData); + } + } + } + // Always return success. + return VSConstants.S_OK; + } + + public int OnBeforeLastDocumentUnlock(uint docCookie, uint dwRDTLockType, uint dwReadLocksRemaining, uint dwEditLocksRemaining) { + if ((0 != dwEditLocksRemaining) || (0 != dwReadLocksRemaining)) { + return VSConstants.S_OK; + } + TextLineEventListener listener; + if (!_documents.TryGetValue(docCookie, out listener) || (null == listener)) { + return VSConstants.S_OK; + } + using (listener) { + _documents.Remove(docCookie); + // Now make sure that the information about this file are up to date (e.g. it is + // possible that Class View shows something strange if the file was closed without + // saving the changes). + HierarchyEventArgs args = new HierarchyEventArgs(listener.FileID.ItemID, listener.FileName); + OnNewFile(listener.FileID.Hierarchy, args); + } + return VSConstants.S_OK; + } + + #endregion + + public void OnIdle() { + foreach (TextLineEventListener listener in _documents.Values) { + listener.OnIdle(); + } + } + + #region IDisposable Members + + public void Dispose() { + // Dispose all the listeners. + foreach (HierarchyListener listener in _hierarchies.Values) { + listener.Dispose(); + } + _hierarchies.Clear(); + + foreach (TextLineEventListener textListener in _documents.Values) { + textListener.Dispose(); + } + _documents.Clear(); + + // Remove this library from the object manager. + if (0 != _objectManagerCookie) { + IVsObjectManager2 mgr = GetPackageService(typeof(SVsObjectManager)) as IVsObjectManager2; + if (null != mgr) { + mgr.UnregisterLibrary(_objectManagerCookie); + } + _objectManagerCookie = 0; + } + + // Unregister this object from the RDT events. + UnregisterRDTEvents(); + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/LibraryNode.cs b/Tools/IronStudio/IronStudio/IronStudio/Navigation/LibraryNode.cs new file mode 100644 index 0000000000..d6328e0b66 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/LibraryNode.cs @@ -0,0 +1,299 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Design; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using VSConstants = Microsoft.VisualStudio.VSConstants; + +namespace Microsoft.IronStudio.Navigation { + + /// + /// Single node inside the tree of the libraries in the object browser or class view. + /// + public class LibraryNode : SimpleObjectList, IVsNavInfoNode, ISimpleObject { + private string _name; + private LibraryNodeCapabilities _capabilities; + private readonly LibraryNodeType _type; + private readonly CommandID _contextMenuID; + private readonly string _tooltip; + private readonly Dictionary _filteredView; + + public LibraryNode(string name) + : this(name, LibraryNodeType.None, LibraryNodeCapabilities.None, null) { } + + public LibraryNode(string name, LibraryNodeType type) + : this(name, type, LibraryNodeCapabilities.None, null) { } + + public LibraryNode(string name, LibraryNodeType type, LibraryNodeCapabilities capabilities, CommandID contextMenuID) { + _capabilities = capabilities; + _contextMenuID = contextMenuID; + _name = name; + _tooltip = name; + _type = type; + _filteredView = new Dictionary(); + } + + public LibraryNode(LibraryNode node) { + _capabilities = node._capabilities; + _contextMenuID = node._contextMenuID; + _name = node._name; + _tooltip = node._tooltip; + _type = node._type; + Children.AddRange(node.Children); + _filteredView = new Dictionary(); + } + + protected void SetCapabilityFlag(LibraryNodeCapabilities flag, bool value) { + if (value) { + _capabilities |= flag; + } else { + _capabilities &= ~flag; + } + } + + /// + /// Get or Set if the node can be deleted. + /// + public bool CanDelete { + get { return (0 != (_capabilities & LibraryNodeCapabilities.AllowDelete)); } + set { SetCapabilityFlag(LibraryNodeCapabilities.AllowDelete, value); } + } + + /// + /// Get or Set if the node can be associated with some source code. + /// + public bool CanGoToSource { + get { return (0 != (_capabilities & LibraryNodeCapabilities.HasSourceContext)); } + set { SetCapabilityFlag(LibraryNodeCapabilities.HasSourceContext, value); } + } + + /// + /// Get or Set if the node can be renamed. + /// + public bool CanRename { + get { return (0 != (_capabilities & LibraryNodeCapabilities.AllowRename)); } + set { SetCapabilityFlag(LibraryNodeCapabilities.AllowRename, value); } + } + + /// + /// + /// + + public override uint Capabilities { get { return (uint)_capabilities; } } + + public string TooltipText { + get { return _tooltip; } + } + + internal void AddNode(LibraryNode node) { + lock (Children) { + Children.Add(node); + } + Update(); + } + + internal void RemoveNode(LibraryNode node) { + lock (Children) { + Children.Remove(node); + } + Update(); + } + + public virtual object BrowseObject { + get { return null; } + } + + public override uint CategoryField(LIB_CATEGORY category) { + uint fieldValue = 0; + switch (category) { + case LIB_CATEGORY.LC_LISTTYPE: { + LibraryNodeType subTypes = LibraryNodeType.None; + foreach (LibraryNode node in Children) { + subTypes |= node._type; + } + fieldValue = (uint)subTypes; + } + break; + case (LIB_CATEGORY)_LIB_CATEGORY2.LC_HIERARCHYTYPE: + fieldValue = (uint)_LIBCAT_HIERARCHYTYPE.LCHT_UNKNOWN; + break; + default: + throw new NotImplementedException(); + } + return fieldValue; + } + + protected virtual LibraryNode Clone() { + return new LibraryNode(this); + } + + /// + /// Performs the operations needed to delete this node. + /// + public virtual void Delete() { + } + + /// + /// Perform a Drag and Drop operation on this node. + /// + public virtual void DoDragDrop(OleDataObject dataObject, uint keyState, uint effect) { + } + + public virtual uint EnumClipboardFormats(_VSOBJCFFLAGS flags, VSOBJCLIPFORMAT[] formats) { + return 0; + } + + public virtual void FillDescription(_VSOBJDESCOPTIONS flags, IVsObjectBrowserDescription3 description) { + description.ClearDescriptionText(); + description.AddDescriptionText3(_name, VSOBDESCRIPTIONSECTION.OBDS_NAME, null); + } + + public IVsSimpleObjectList2 FilterView(uint filterType) { + var libraryNodeType = (LibraryNodeType)filterType; + LibraryNode filtered = null; + if (_filteredView.TryGetValue(libraryNodeType, out filtered)) { + return filtered as IVsSimpleObjectList2; + } + filtered = this.Clone(); + for (int i = 0; i < filtered.Children.Count; ) { + if (0 == (filtered.Children[i]._type & libraryNodeType)) { + filtered.Children.RemoveAt(i); + } else { + i += 1; + } + } + _filteredView.Add(libraryNodeType, filtered); + return filtered as IVsSimpleObjectList2; + } + + public virtual void GotoSource(VSOBJGOTOSRCTYPE gotoType) { + // Do nothing. + } + + public string Name { + get { return _name; } + } + + public virtual string GetTextRepresentation(VSTREETEXTOPTIONS options) { + return Name; + } + + public LibraryNodeType NodeType { + get { return _type; } + } + + /// + /// Finds the source files associated with this node. + /// + /// The hierarchy containing the items. + /// The item id of the item. + /// Number of items. + public virtual void SourceItems(out IVsHierarchy hierarchy, out uint itemId, out uint itemsCount) { + hierarchy = null; + itemId = 0; + itemsCount = 0; + } + + public virtual void Rename(string newName, uint flags) { + this._name = newName; + } + + public virtual string UniqueName { + get { return Name; } + } + + public CommandID ContextMenuID { + get { + return _contextMenuID; + } + } + + public virtual StandardGlyphGroup GlyphType { + get { + return StandardGlyphGroup.GlyphGroupModule; + } + } + + public VSTREEDISPLAYDATA DisplayData { + get { + var res = new VSTREEDISPLAYDATA(); + res.Image = res.SelectedImage = (ushort)GlyphType; + return res; + } + } + + #region IVsNavInfoNode Members + + int IVsNavInfoNode.get_Name(out string pbstrName) { + pbstrName = UniqueName; + return VSConstants.S_OK; + } + + int IVsNavInfoNode.get_Type(out uint pllt) { + pllt = (uint)_type; + return VSConstants.S_OK; + } + + #endregion + + /// + /// Enumeration of the capabilities of a node. It is possible to combine different values + /// to support more capabilities. + /// This enumeration is a copy of _LIB_LISTCAPABILITIES with the Flags attribute set. + /// + [Flags()] + public enum LibraryNodeCapabilities { + None = _LIB_LISTCAPABILITIES.LLC_NONE, + HasBrowseObject = _LIB_LISTCAPABILITIES.LLC_HASBROWSEOBJ, + HasDescriptionPane = _LIB_LISTCAPABILITIES.LLC_HASDESCPANE, + HasSourceContext = _LIB_LISTCAPABILITIES.LLC_HASSOURCECONTEXT, + HasCommands = _LIB_LISTCAPABILITIES.LLC_HASCOMMANDS, + AllowDragDrop = _LIB_LISTCAPABILITIES.LLC_ALLOWDRAGDROP, + AllowRename = _LIB_LISTCAPABILITIES.LLC_ALLOWRENAME, + AllowDelete = _LIB_LISTCAPABILITIES.LLC_ALLOWDELETE, + AllowSourceControl = _LIB_LISTCAPABILITIES.LLC_ALLOWSCCOPS, + } + + /// + /// Enumeration of the possible types of node. The type of a node can be the combination + /// of one of more of these values. + /// This is actually a copy of the _LIB_LISTTYPE enumeration with the difference that the + /// Flags attribute is set so that it is possible to specify more than one value. + /// + [Flags()] + public enum LibraryNodeType { + None = 0, + Hierarchy = _LIB_LISTTYPE.LLT_HIERARCHY, + Namespaces = _LIB_LISTTYPE.LLT_NAMESPACES, + Classes = _LIB_LISTTYPE.LLT_CLASSES, + Members = _LIB_LISTTYPE.LLT_MEMBERS, + Package = _LIB_LISTTYPE.LLT_PACKAGE, + PhysicalContainer = _LIB_LISTTYPE.LLT_PHYSICALCONTAINERS, + Containment = _LIB_LISTTYPE.LLT_CONTAINMENT, + ContainedBy = _LIB_LISTTYPE.LLT_CONTAINEDBY, + UsesClasses = _LIB_LISTTYPE.LLT_USESCLASSES, + UsedByClasses = _LIB_LISTTYPE.LLT_USEDBYCLASSES, + NestedClasses = _LIB_LISTTYPE.LLT_NESTEDCLASSES, + InheritedInterface = _LIB_LISTTYPE.LLT_INHERITEDINTERFACES, + InterfaceUsedByClasses = _LIB_LISTTYPE.LLT_INTERFACEUSEDBYCLASSES, + Definitions = _LIB_LISTTYPE.LLT_DEFINITIONS, + References = _LIB_LISTTYPE.LLT_REFERENCES, + DeferExpansion = _LIB_LISTTYPE.LLT_DEFEREXPANSION, + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/LibraryTask.cs b/Tools/IronStudio/IronStudio/IronStudio/Navigation/LibraryTask.cs new file mode 100644 index 0000000000..9379733e16 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/LibraryTask.cs @@ -0,0 +1,52 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronStudio.Navigation { + /// + /// Class storing the data about a parsing task on a language module. + /// A module in dynamic languages is a source file, so here we use the file name to + /// identify it. + /// + public class LibraryTask { + private string _fileName; + private ITextBuffer _textBuffer; + private ModuleId _moduleId; + + public LibraryTask(string fileName, ITextBuffer textBuffer, ModuleId moduleId) { + _fileName = fileName; + _textBuffer = textBuffer; + _moduleId = moduleId; + } + + public string FileName { + get { return _fileName; } + } + + public ModuleId ModuleID { + get { return _moduleId; } + set { _moduleId = value; } + } + + public ITextBuffer TextBuffer { + get { return _textBuffer; } + } + } + +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/ModuleId.cs b/Tools/IronStudio/IronStudio/IronStudio/Navigation/ModuleId.cs new file mode 100644 index 0000000000..f4645870b4 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/ModuleId.cs @@ -0,0 +1,59 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.IronStudio.Navigation { + /// + /// Class used to identify a module. The module is identified using the hierarchy that + /// contains it and its item id inside the hierarchy. + /// + public sealed class ModuleId { + private IVsHierarchy _ownerHierarchy; + private uint _itemId; + + public ModuleId(IVsHierarchy owner, uint id) { + _ownerHierarchy = owner; + _itemId = id; + } + + public IVsHierarchy Hierarchy { + get { return _ownerHierarchy; } + } + + public uint ItemID { + get { return _itemId; } + } + + public override int GetHashCode() { + int hash = 0; + if (null != _ownerHierarchy) { + hash = _ownerHierarchy.GetHashCode(); + } + hash = hash ^ (int)_itemId; + return hash; + } + + public override bool Equals(object obj) { + ModuleId other = obj as ModuleId; + if (null == obj) { + return false; + } + if (!_ownerHierarchy.Equals(other._ownerHierarchy)) { + return false; + } + return (_itemId == other._itemId); + } + } +} \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/Resources.Designer.cs b/Tools/IronStudio/IronStudio/IronStudio/Navigation/Resources.Designer.cs new file mode 100644 index 0000000000..309ff5a3cb --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/Resources.Designer.cs @@ -0,0 +1,90 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:2.0.50727.1434 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.IronStudio.Navigation { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "2.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.IronStudio.Navigation.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Base Types. + /// + internal static string BaseTypesNodeText { + get { + return ResourceManager.GetString("BaseTypesNodeText", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Insert Snippet. + /// + internal static string InsertSnippet { + get { + return ResourceManager.GetString("InsertSnippet", resourceCulture); + } + } + + /// + /// Looks up a localized string similar to Surround With.... + /// + internal static string SurroundWith { + get { + return ResourceManager.GetString("SurroundWith", resourceCulture); + } + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/Resources.resx b/Tools/IronStudio/IronStudio/IronStudio/Navigation/Resources.resx new file mode 100644 index 0000000000..4c3229fd70 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/Resources.resx @@ -0,0 +1,110 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 1.3 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.3500.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Base Types + + + Insert Snippet + + + Surround With... + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/SimpleObject.cs b/Tools/IronStudio/IronStudio/IronStudio/Navigation/SimpleObject.cs new file mode 100644 index 0000000000..8f9ecf4ca2 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/SimpleObject.cs @@ -0,0 +1,98 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.IronStudio.Navigation { + class SimpleObject : ISimpleObject { + #region ISimpleObject Members + + public virtual bool CanDelete { + get { return false; } + } + + public virtual bool CanGoToSource { + get { return false; } + } + + public virtual bool CanRename { + get { return false; } + } + + public virtual string Name { + get { return String.Empty; } + } + + public virtual string UniqueName { + get { return String.Empty; } + } + + public virtual string GetTextRepresentation(VSTREETEXTOPTIONS options) { + return Name; + } + + public virtual string TooltipText { + get { return String.Empty; } + } + + public virtual object BrowseObject { + get { return null; } + } + + public virtual System.ComponentModel.Design.CommandID ContextMenuID { + get { return null; } + } + + public virtual VSTREEDISPLAYDATA DisplayData { + get { return new VSTREEDISPLAYDATA(); } + } + + public virtual uint CategoryField(LIB_CATEGORY lIB_CATEGORY) { + return 0; + } + + public virtual void Delete() { + } + + public virtual void DoDragDrop(OleDataObject dataObject, uint grfKeyState, uint pdwEffect) { + } + + public virtual void Rename(string pszNewName, uint grfFlags) { + } + + public virtual void GotoSource(VSOBJGOTOSRCTYPE SrcType) { + } + + public virtual void SourceItems(out IVsHierarchy ppHier, out uint pItemid, out uint pcItems) { + ppHier = null; + pItemid = 0; + pcItems = 0; + } + + public virtual uint EnumClipboardFormats(_VSOBJCFFLAGS _VSOBJCFFLAGS, VSOBJCLIPFORMAT[] rgcfFormats) { + return 0; + } + + public virtual void FillDescription(_VSOBJDESCOPTIONS _VSOBJDESCOPTIONS, IVsObjectBrowserDescription3 pobDesc) { + } + + public virtual IVsSimpleObjectList2 FilterView(uint ListType) { + return null; + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/SimpleObjectList.cs b/Tools/IronStudio/IronStudio/IronStudio/Navigation/SimpleObjectList.cs new file mode 100644 index 0000000000..eeeadff938 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/SimpleObjectList.cs @@ -0,0 +1,320 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Design; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.IronStudio.Navigation { + /// + /// Represents a simple list which the VS UI can query for items. + /// + /// VS assumes that these lists do not change once VS has gotten ahold of them. Therefore if the + /// list is changing over time it should be thrown away and a new list should be placed in the parent. + /// + public class SimpleObjectList : IVsSimpleObjectList2 where T : ISimpleObject { + private readonly List _children; + private uint _updateCount; + + public SimpleObjectList() { + _children = new List(); + } + + public List Children { + get { + return _children; + } + } + + public void Update() { + _updateCount++; + } + + public uint UpdateCount { + get { return _updateCount; } + set { _updateCount = value; } + } + + public const uint NullIndex = (uint)0xFFFFFFFF; + + int IVsSimpleObjectList2.CanDelete(uint index, out int pfOK) { + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + pfOK = _children[(int)index].CanDelete ? 1 : 0; + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.CanGoToSource(uint index, VSOBJGOTOSRCTYPE SrcType, out int pfOK) { + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + pfOK = _children[(int)index].CanGoToSource ? 1 : 0; + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.CanRename(uint index, string pszNewName, out int pfOK) { + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + pfOK = _children[(int)index].CanRename ? 1 : 0; + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.CountSourceItems(uint index, out IVsHierarchy ppHier, out uint pItemid, out uint pcItems) { + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + _children[(int)index].SourceItems(out ppHier, out pItemid, out pcItems); + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.DoDelete(uint index, uint grfFlags) { + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + _children[(int)index].Delete(); + _children.RemoveAt((int)index); + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.DoDragDrop(uint index, IDataObject pDataObject, uint grfKeyState, ref uint pdwEffect) { + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + OleDataObject dataObject = new OleDataObject(pDataObject); + _children[(int)index].DoDragDrop(dataObject, grfKeyState, pdwEffect); + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.DoRename(uint index, string pszNewName, uint grfFlags) { + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + _children[(int)index].Rename(pszNewName, grfFlags); + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.EnumClipboardFormats(uint index, uint grfFlags, uint celt, VSOBJCLIPFORMAT[] rgcfFormats, uint[] pcActual) { + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + uint copied = _children[(int)index].EnumClipboardFormats((_VSOBJCFFLAGS)grfFlags, rgcfFormats); + if ((null != pcActual) && (pcActual.Length > 0)) { + pcActual[0] = copied; + } + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.FillDescription2(uint index, uint grfOptions, IVsObjectBrowserDescription3 pobDesc) { + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + _children[(int)index].FillDescription((_VSOBJDESCOPTIONS)grfOptions, pobDesc); + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.GetBrowseObject(uint index, out object ppdispBrowseObj) { + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + ppdispBrowseObj = _children[(int)index].BrowseObject; + if (null == ppdispBrowseObj) { + return VSConstants.E_NOTIMPL; + } + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.GetCapabilities2(out uint pgrfCapabilities) { + pgrfCapabilities = (uint)Capabilities; + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.GetCategoryField2(uint index, int Category, out uint pfCatField) { + if (NullIndex == index) { + pfCatField = CategoryField((LIB_CATEGORY)Category); + } else if (index < (uint)_children.Count) { + pfCatField = _children[(int)index].CategoryField((LIB_CATEGORY)Category); + } else { + throw new ArgumentOutOfRangeException("index"); + } + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.GetClipboardFormat(uint index, uint grfFlags, FORMATETC[] pFormatetc, STGMEDIUM[] pMedium) { + return VSConstants.E_NOTIMPL; + } + + int IVsSimpleObjectList2.GetContextMenu(uint index, out Guid pclsidActive, out int pnMenuId, out IOleCommandTarget ppCmdTrgtActive) { + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + CommandID commandId = _children[(int)index].ContextMenuID; + if (null == commandId) { + pclsidActive = Guid.Empty; + pnMenuId = 0; + ppCmdTrgtActive = null; + return VSConstants.E_NOTIMPL; + } + pclsidActive = commandId.Guid; + pnMenuId = commandId.ID; + ppCmdTrgtActive = _children[(int)index] as IOleCommandTarget; + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.GetDisplayData(uint index, VSTREEDISPLAYDATA[] pData) { + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + pData[0] = _children[(int)index].DisplayData; + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.GetExpandable3(uint index, uint ListTypeExcluded, out int pfExpandable) { + // There is a not empty implementation of GetCategoryField2, so this method should + // return E_NOTIMPL. + pfExpandable = 0; + return VSConstants.E_NOTIMPL; + } + + int IVsSimpleObjectList2.GetExtendedClipboardVariant(uint index, uint grfFlags, VSOBJCLIPFORMAT[] pcfFormat, out object pvarFormat) { + pvarFormat = null; + return VSConstants.E_NOTIMPL; + } + + int IVsSimpleObjectList2.GetFlags(out uint pFlags) { + pFlags = (uint)Flags; + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.GetItemCount(out uint pCount) { + pCount = (uint)_children.Count; + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.GetList2(uint index, uint ListType, uint flags, VSOBSEARCHCRITERIA2[] pobSrch, out IVsSimpleObjectList2 ppIVsSimpleObjectList2) { + // TODO: Use the flags and list type to actually filter the result. + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + ppIVsSimpleObjectList2 = _children[(int)index].FilterView(ListType); + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.GetMultipleSourceItems(uint index, uint grfGSI, uint cItems, VSITEMSELECTION[] rgItemSel) { + return VSConstants.E_NOTIMPL; + } + + int IVsSimpleObjectList2.GetNavInfo(uint index, out IVsNavInfo ppNavInfo) { + ppNavInfo = null; + return VSConstants.E_NOTIMPL; + } + + int IVsSimpleObjectList2.GetNavInfoNode(uint index, out IVsNavInfoNode ppNavInfoNode) { + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + ppNavInfoNode = _children[(int)index] as IVsNavInfoNode; + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.GetProperty(uint index, int propid, out object pvar) { + pvar = null; + return VSConstants.E_NOTIMPL; + } + + int IVsSimpleObjectList2.GetSourceContextWithOwnership(uint index, out string pbstrFilename, out uint pulLineNum) { + pbstrFilename = null; + pulLineNum = (uint)0; + return VSConstants.E_NOTIMPL; + } + + int IVsSimpleObjectList2.GetTextWithOwnership(uint index, VSTREETEXTOPTIONS tto, out string pbstrText) { + // TODO: make use of the text option. + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + pbstrText = _children[(int)index].GetTextRepresentation(tto); + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.GetTipTextWithOwnership(uint index, VSTREETOOLTIPTYPE eTipType, out string pbstrText) { + // TODO: Make use of the tooltip type. + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + pbstrText = _children[(int)index].TooltipText; + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.GetUserContext(uint index, out object ppunkUserCtx) { + ppunkUserCtx = null; + return VSConstants.E_NOTIMPL; + } + + int IVsSimpleObjectList2.GoToSource(uint index, VSOBJGOTOSRCTYPE SrcType) { + if (index >= (uint)_children.Count) { + throw new ArgumentOutOfRangeException("index"); + } + _children[(int)index].GotoSource(SrcType); + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.LocateNavInfoNode(IVsNavInfoNode pNavInfoNode, out uint pulIndex) { + if (null == pNavInfoNode) { + throw new ArgumentNullException("pNavInfoNode"); + } + pulIndex = NullIndex; + string nodeName; + ErrorHandler.ThrowOnFailure(pNavInfoNode.get_Name(out nodeName)); + for (int i = 0; i < _children.Count; i++) { + if (0 == string.Compare(_children[i].UniqueName, nodeName, StringComparison.OrdinalIgnoreCase)) { + pulIndex = (uint)i; + return VSConstants.S_OK; + } + } + return VSConstants.S_FALSE; + } + + int IVsSimpleObjectList2.OnClose(VSTREECLOSEACTIONS[] ptca) { + // Do Nothing. + return VSConstants.S_OK; + } + + int IVsSimpleObjectList2.QueryDragDrop(uint index, IDataObject pDataObject, uint grfKeyState, ref uint pdwEffect) { + return VSConstants.E_NOTIMPL; + } + + int IVsSimpleObjectList2.ShowHelp(uint index) { + return VSConstants.E_NOTIMPL; + } + + int IVsSimpleObjectList2.UpdateCounter(out uint pCurUpdate) { + pCurUpdate = _updateCount; + return VSConstants.S_OK; + } + + public virtual uint Capabilities { get { return 0; } } + + public virtual _VSTREEFLAGS Flags { get { return 0; } } + + public virtual uint CategoryField(LIB_CATEGORY lIB_CATEGORY) { + return 0; + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/SnippetsEnumerator.cs b/Tools/IronStudio/IronStudio/IronStudio/Navigation/SnippetsEnumerator.cs new file mode 100644 index 0000000000..699a6bc4a2 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/SnippetsEnumerator.cs @@ -0,0 +1,125 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Runtime.InteropServices; + +using Microsoft.VisualStudio.TextManager.Interop; +using ErrorHandler = Microsoft.VisualStudio.ErrorHandler; +using VSConstants = Microsoft.VisualStudio.VSConstants; + + +namespace Microsoft.IronStudio.Navigation { + public class SnippetsEnumerator : IEnumerable { + private IVsTextManager2 _textManager; + private Guid _languageGuid; + private bool _shortcutOnly; + + /// + /// This structure is used to facilitate the interop calls to the method + /// exposed by IVsExpansionEnumeration. + /// + [StructLayout(LayoutKind.Sequential)] + private struct ExpansionBuffer { + public IntPtr pathPtr; + public IntPtr titlePtr; + public IntPtr shortcutPtr; + public IntPtr descriptionPtr; + } + + public SnippetsEnumerator(IVsTextManager2 textManager, Guid languageGuid) { + if (null == textManager) { + throw new ArgumentNullException("textManager"); + } + _textManager = textManager; + _languageGuid = languageGuid; + } + + public bool ShortcutOnly { + get { return _shortcutOnly; } + set { _shortcutOnly = value; } + } + + #region IEnumerable Members + public IEnumerator GetEnumerator() { + IVsExpansionManager expansionManager; + ErrorHandler.ThrowOnFailure(_textManager.GetExpansionManager(out expansionManager)); + + IVsExpansionEnumeration enumerator; + int onlyShortcut = (this.ShortcutOnly ? 1 : 0); + ErrorHandler.ThrowOnFailure(expansionManager.EnumerateExpansions(_languageGuid, onlyShortcut, null, 0, 0, 0, out enumerator)); + + ExpansionBuffer buffer = new ExpansionBuffer(); + GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + try { + int hr = VSConstants.S_OK; + uint fetched; + while (VSConstants.S_OK == (hr = enumerator.Next(1, new IntPtr[] { handle.AddrOfPinnedObject() }, out fetched))) { + buffer = (ExpansionBuffer)handle.Target; + try { + handle.Free(); + if (IntPtr.Zero != buffer.shortcutPtr) { + VsExpansion expansion = new VsExpansion(); + expansion.shortcut = Marshal.PtrToStringBSTR(buffer.shortcutPtr); + if (IntPtr.Zero != buffer.descriptionPtr) { + expansion.description = Marshal.PtrToStringBSTR(buffer.descriptionPtr); + } + if (IntPtr.Zero != buffer.pathPtr) { + expansion.path = Marshal.PtrToStringBSTR(buffer.pathPtr); + } + if (IntPtr.Zero != buffer.titlePtr) { + expansion.title = Marshal.PtrToStringBSTR(buffer.titlePtr); + } + yield return expansion; + handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + } + } finally { + if (IntPtr.Zero != buffer.descriptionPtr) { + Marshal.FreeBSTR(buffer.descriptionPtr); + buffer.descriptionPtr = IntPtr.Zero; + } + if (IntPtr.Zero != buffer.pathPtr) { + Marshal.FreeBSTR(buffer.pathPtr); + buffer.pathPtr = IntPtr.Zero; + } + if (IntPtr.Zero != buffer.shortcutPtr) { + Marshal.FreeBSTR(buffer.shortcutPtr); + buffer.shortcutPtr = IntPtr.Zero; + } + if (IntPtr.Zero != buffer.titlePtr) { + Marshal.FreeBSTR(buffer.titlePtr); + buffer.titlePtr = IntPtr.Zero; + } + } + } + ErrorHandler.ThrowOnFailure(hr); + } finally { + if (handle.IsAllocated) { + handle.Free(); + } + } + } + #endregion + + #region IEnumerable Members + + IEnumerator IEnumerable.GetEnumerator() { + throw new NotImplementedException(); + } + + #endregion + } +} \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/TextLineEventListener.cs b/Tools/IronStudio/IronStudio/IronStudio/Navigation/TextLineEventListener.cs new file mode 100644 index 0000000000..4ad33c69a1 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/TextLineEventListener.cs @@ -0,0 +1,103 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.TextManager.Interop; + +namespace Microsoft.IronStudio.Navigation { + internal class TextLineEventListener : IVsTextLinesEvents, IDisposable { + private const int _defaultDelay = 2000; + private string _fileName; + private ModuleId _fileId; + private IVsTextLines _buffer; + private bool _isDirty; + private IConnectionPoint _connectionPoint; + private uint _connectionCookie; + + public TextLineEventListener(IVsTextLines buffer, string fileName, ModuleId id) + { + _buffer = buffer; + _fileId = id; + _fileName = fileName; + IConnectionPointContainer container = buffer as IConnectionPointContainer; + if (null != container) { + Guid eventsGuid = typeof(IVsTextLinesEvents).GUID; + container.FindConnectionPoint(ref eventsGuid, out _connectionPoint); + _connectionPoint.Advise(this as IVsTextLinesEvents, out _connectionCookie); + } + } + + #region Properties + public ModuleId FileID { + get { return _fileId; } + } + public string FileName { + get { return _fileName; } + set { _fileName = value; } + } + #endregion + + #region Events + public event EventHandler OnFileChanged; + + public event TextLineChangeEvent OnFileChangedImmediate; + + #endregion + + #region IVsTextLinesEvents Members + void IVsTextLinesEvents.OnChangeLineAttributes(int iFirstLine, int iLastLine) { + // Do Nothing + } + + void IVsTextLinesEvents.OnChangeLineText(TextLineChange[] pTextLineChange, int fLast) { + TextLineChangeEvent eh = OnFileChangedImmediate; + if (null != eh) { + eh(this, pTextLineChange, fLast); + } + + _isDirty = true; + } + #endregion + + #region IDisposable Members + public void Dispose() { + if ((null != _connectionPoint) && (0 != _connectionCookie)) { + _connectionPoint.Unadvise(_connectionCookie); + } + _connectionCookie = 0; + _connectionPoint = null; + + _buffer = null; + _fileId = null; + } + #endregion + + #region Idle time processing + public void OnIdle() { + if (!_isDirty) { + return; + } + var onFileChanged = OnFileChanged; + if (null != onFileChanged) { + HierarchyEventArgs args = new HierarchyEventArgs(_fileId.ItemID, _fileName); + args.TextBuffer = _buffer; + onFileChanged(_fileId.Hierarchy, args); + } + + _isDirty = false; + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Navigation/TextViewWrapper.cs b/Tools/IronStudio/IronStudio/IronStudio/Navigation/TextViewWrapper.cs new file mode 100644 index 0000000000..6cbda3259a --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Navigation/TextViewWrapper.cs @@ -0,0 +1,328 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.TextManager.Interop; +using ErrorHandler = Microsoft.VisualStudio.ErrorHandler; +using VSConstants = Microsoft.VisualStudio.VSConstants; + +namespace Microsoft.IronStudio.Navigation { + public class TextViewWrapper : IVsTextView, IConnectionPointContainer { + private IVsIntellisenseHost _intellisenseHost; + private IVsContainedLanguageHost _languageHost; + private IVsTextBufferCoordinator _bufferCoordinator; + private IOleCommandTarget _nextTarget; + private IOleCommandTarget _installedFilter; + + // This class is an helper class that provides an empty implementation of + // IConnectionPoint. Is is needed only because some clients of the text view + // need to subscribe for events, but this implementation does not raise + // any event. + private class TextViewConnectionPoint : IConnectionPoint { + private uint _nextCoockie; + + #region IConnectionPoint Members + public void Advise(object pUnkSink, out uint pdwCookie) { + pdwCookie = ++_nextCoockie; + } + + public void EnumConnections(out IEnumConnections ppEnum) { + throw new NotImplementedException(); + } + + public void GetConnectionInterface(out Guid pIID) { + throw new NotImplementedException(); + } + + public void GetConnectionPointContainer(out IConnectionPointContainer ppCPC) { + throw new NotImplementedException(); + } + + public void Unadvise(uint dwCookie) { + // Do Nothing + } + + #endregion + } + + public TextViewWrapper(IVsContainedLanguageHost languageHost, IVsIntellisenseHost intellisenseHost, IVsTextBufferCoordinator coordinator, IOleCommandTarget nextTarget) { + if (null == intellisenseHost) { + throw new ArgumentNullException("buffer"); + } + _intellisenseHost = intellisenseHost; + _bufferCoordinator = coordinator; + _languageHost = languageHost; + _nextTarget = nextTarget; + } + + public IVsTextViewFilter InstalledFilter { + get { + return _installedFilter as IVsTextViewFilter; + } + } + + public TextSpan GetPrimarySpan(TextSpan secondary) { + TextSpan[] primary = new TextSpan[1]; + ErrorHandler.ThrowOnFailure(_bufferCoordinator.MapSecondaryToPrimarySpan(secondary, primary)); + return primary[0]; + } + + #region IVsTextView Members + public int AddCommandFilter(IOleCommandTarget pNewCmdTarg, out IOleCommandTarget ppNextCmdTarg) { + ppNextCmdTarg = _nextTarget; + _installedFilter = pNewCmdTarg; + return VSConstants.S_OK; + } + + public int CenterColumns(int iLine, int iLeftCol, int iColCount) { + return VSConstants.S_OK; + } + + public int CenterLines(int iTopLine, int iCount) { + return VSConstants.S_OK; + } + + public int ClearSelection(int fMoveToAnchor) { + throw new NotImplementedException(); + } + + public int CloseView() { + _intellisenseHost = null; + _bufferCoordinator = null; + _languageHost = null; + return VSConstants.S_OK; + } + + public int EnsureSpanVisible(TextSpan span) { + if (null == _languageHost) { + return VSConstants.S_OK; + } + return _languageHost.EnsureSpanVisible(span); + } + + public int GetBuffer(out IVsTextLines ppBuffer) { + return _bufferCoordinator.GetSecondaryBuffer(out ppBuffer); + } + + public int GetCaretPos(out int piLine, out int piColumn) { + TextSpan originalSpan = new TextSpan(); + ErrorHandler.ThrowOnFailure( + _intellisenseHost.GetContextCaretPos(out originalSpan.iStartLine, out originalSpan.iStartIndex)); + originalSpan.iEndLine = originalSpan.iStartLine; + originalSpan.iEndIndex = originalSpan.iStartIndex; + TextSpan[] convertedSpan = new TextSpan[1]; + ErrorHandler.ThrowOnFailure(_bufferCoordinator.MapPrimaryToSecondarySpan(originalSpan, convertedSpan)); + piLine = convertedSpan[0].iStartLine; + piColumn = convertedSpan[0].iStartIndex; + return VSConstants.S_OK; + } + + public int GetLineAndColumn(int iPos, out int piLine, out int piIndex) { + IVsTextLines buffer; + ErrorHandler.ThrowOnFailure(_bufferCoordinator.GetSecondaryBuffer(out buffer)); + return buffer.GetLineIndexOfPosition(iPos, out piLine, out piIndex); + } + + public int GetLineHeight(out int piLineHeight) { + throw new NotImplementedException(); + } + + public int GetNearestPosition(int iLine, int iCol, out int piPos, out int piVirtualSpaces) { + throw new NotImplementedException(); + } + + public int GetPointOfLineColumn(int iLine, int iCol, Microsoft.VisualStudio.OLE.Interop.POINT[] ppt) { + throw new NotImplementedException(); + } + + public int GetScrollInfo(int iBar, out int piMinUnit, out int piMaxUnit, out int piVisibleUnits, out int piFirstVisibleUnit) { + throw new NotImplementedException(); + } + + public int GetSelectedText(out string pbstrText) { + TextSpan[] span = new TextSpan[1]; + ErrorHandler.ThrowOnFailure(_intellisenseHost.GetContextSelection(span)); + TextSpan[] convertedSpan = new TextSpan[1]; + ErrorHandler.ThrowOnFailure(_bufferCoordinator.MapPrimaryToSecondarySpan(span[0], convertedSpan)); + IVsTextLines buffer; + ErrorHandler.ThrowOnFailure(_bufferCoordinator.GetSecondaryBuffer(out buffer)); + return buffer.GetLineText(convertedSpan[0].iStartLine, convertedSpan[0].iStartIndex, convertedSpan[0].iEndLine, convertedSpan[0].iEndIndex, out pbstrText); + } + + public int GetSelection(out int piAnchorLine, out int piAnchorCol, out int piEndLine, out int piEndCol) { + TextSpan[] span = new TextSpan[1]; + ErrorHandler.ThrowOnFailure(_intellisenseHost.GetContextSelection(span)); + TextSpan[] convertedSpan = new TextSpan[1]; + ErrorHandler.ThrowOnFailure(_bufferCoordinator.MapPrimaryToSecondarySpan(span[0], convertedSpan)); + piAnchorLine = convertedSpan[0].iStartLine; + piAnchorCol = convertedSpan[0].iStartIndex; + piEndLine = convertedSpan[0].iEndLine; + piEndCol = convertedSpan[0].iEndIndex; + return VSConstants.S_OK; + } + + public int GetSelectionDataObject(out Microsoft.VisualStudio.OLE.Interop.IDataObject ppIDataObject) { + throw new NotImplementedException(); + } + + public TextSelMode GetSelectionMode() { + throw new NotImplementedException(); + } + + public int GetSelectionSpan(TextSpan[] pSpan) { + TextSpan[] primarySpan = new TextSpan[1]; + ErrorHandler.ThrowOnFailure(_intellisenseHost.GetContextSelection(primarySpan)); + return _bufferCoordinator.MapPrimaryToSecondarySpan(primarySpan[0], pSpan); + } + + public int GetTextStream(int iTopLine, int iTopCol, int iBottomLine, int iBottomCol, out string pbstrText) { + throw new NotImplementedException(); + } + + public IntPtr GetWindowHandle() { + IntPtr hWnd; + ErrorHandler.ThrowOnFailure(_intellisenseHost.GetHostWindow(out hWnd)); + return hWnd; + } + + public int GetWordExtent(int iLine, int iCol, uint dwFlags, TextSpan[] pSpan) { + throw new NotImplementedException(); + } + + public int HighlightMatchingBrace(uint dwFlags, uint cSpans, TextSpan[] rgBaseSpans) { + if ((null == rgBaseSpans) || (rgBaseSpans.Length == 0)) { + throw new ArgumentNullException("rgBaseSpans"); + } + if ((uint)rgBaseSpans.Length != cSpans) { + throw new System.ArgumentOutOfRangeException("cSpans"); + } + TextSpan[] convertedSpans = new TextSpan[cSpans]; + TextSpan[] workingCopy = new TextSpan[1]; + for (int i = 0; i < cSpans; ++i) { + ErrorHandler.ThrowOnFailure(_bufferCoordinator.MapSecondaryToPrimarySpan(rgBaseSpans[i], workingCopy)); + convertedSpans[i] = workingCopy[0]; + } + return _intellisenseHost.HighlightMatchingBrace(dwFlags, cSpans, convertedSpans); + } + + public int Initialize(IVsTextLines pBuffer, IntPtr hwndParent, uint InitFlags, INITVIEW[] pInitView) { + return VSConstants.S_OK; + } + + public int PositionCaretForEditing(int iLine, int cIndentLevels) { + throw new NotImplementedException(); + } + + public int RemoveCommandFilter(Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget pCmdTarg) { + _installedFilter = null; + return VSConstants.S_OK; + } + + public int ReplaceTextOnLine(int iLine, int iStartCol, int iCharsToReplace, string pszNewText, int iNewLen) { + IVsTextLines buffer; + ErrorHandler.ThrowOnFailure(_bufferCoordinator.GetSecondaryBuffer(out buffer)); + GCHandle handle = GCHandle.Alloc(pszNewText, GCHandleType.Pinned); + try { + TextSpan[] span = new TextSpan[1]; + IntPtr textPtr = handle.AddrOfPinnedObject(); + int textLen = string.IsNullOrEmpty(pszNewText) ? 0 : pszNewText.Length; + ErrorHandler.ThrowOnFailure( + buffer.ReplaceLines(iLine, iStartCol, iLine, iStartCol + iCharsToReplace, textPtr, textLen, span)); + } finally { + if ((null != handle) && (handle.IsAllocated)) { + handle.Free(); + } + } + return VSConstants.S_OK; + } + + public int RestrictViewRange(int iMinLine, int iMaxLine, IVsViewRangeClient pClient) { + throw new NotImplementedException(); + } + + public int SendExplicitFocus() { + throw new NotImplementedException(); + } + + public int SetBuffer(IVsTextLines pBuffer) { + return VSConstants.S_OK; + } + + public int SetCaretPos(int iLine, int iColumn) { + TextSpan original = new TextSpan(); + original.iStartLine = original.iEndLine = iLine; + original.iStartIndex = original.iEndIndex = iColumn; + TextSpan[] converted = new TextSpan[1]; + ErrorHandler.ThrowOnFailure(_bufferCoordinator.MapSecondaryToPrimarySpan(original, converted)); + return _intellisenseHost.SetContextCaretPos(converted[0].iStartLine, converted[0].iStartIndex); + } + + public int SetScrollPosition(int iBar, int iFirstVisibleUnit) { + throw new NotImplementedException(); + } + + public int SetSelection(int iAnchorLine, int iAnchorCol, int iEndLine, int iEndCol) { + TextSpan original = new TextSpan(); + original.iStartLine = iAnchorLine; + original.iStartIndex = iAnchorCol; + original.iEndLine = iEndLine; + original.iEndIndex = iEndCol; + TextSpan[] converted = new TextSpan[1]; + ErrorHandler.ThrowOnFailure(_bufferCoordinator.MapSecondaryToPrimarySpan(original, converted)); + return _intellisenseHost.SetContextSelection(converted[0].iStartLine, converted[0].iStartIndex, converted[0].iEndLine, converted[0].iEndIndex); + } + + public int SetSelectionMode(TextSelMode iSelMode) { + throw new NotImplementedException(); + } + + public int SetTopLine(int iBaseLine) { + throw new NotImplementedException(); + } + + public int UpdateCompletionStatus(IVsCompletionSet pCompSet, uint dwFlags) { + return _intellisenseHost.UpdateCompletionStatus(pCompSet, dwFlags); + } + + public int UpdateTipWindow(IVsTipWindow pTipWindow, uint dwFlags) { + return _intellisenseHost.UpdateTipWindow(pTipWindow, dwFlags); + } + + public int UpdateViewFrameCaption() { + return VSConstants.S_OK; + } + #endregion + + #region IConnectionPointContainer Members + void IConnectionPointContainer.EnumConnectionPoints(out IEnumConnectionPoints ppEnum) { + throw new NotImplementedException(); + } + + private TextViewConnectionPoint connectionPoint; + void IConnectionPointContainer.FindConnectionPoint(ref Guid riid, out IConnectionPoint ppCP) { + ppCP = null; + if (typeof(IVsTextViewEvents).GUID == riid) { + if (null == connectionPoint) { + connectionPoint = new TextViewConnectionPoint(); + } + ppCP = connectionPoint; + return; + } + throw new ArgumentOutOfRangeException("riid"); + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/BaseSearchPathNode.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/BaseSearchPathNode.cs new file mode 100644 index 0000000000..3cf1a1229c --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/BaseSearchPathNode.cs @@ -0,0 +1,107 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Text; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Project; +using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants; +using VsCommands = Microsoft.VisualStudio.VSConstants.VSStd97CmdID; + +namespace Microsoft.IronStudio.Project { + /// + /// Base class for Search Path nodes. + /// + public abstract class BaseSearchPathNode : CommonFolderNode { + protected CommonProjectNode _project; + private string _caption; + + public BaseSearchPathNode(CommonProjectNode project, string path, ProjectElement element) + : base(project, path, element) { + _project = project; + this.VirtualNodeName = path; + this.ExcludeNodeFromScc = true; + } + + /// + /// Show friendly node caption - relative path or normalized absolute path. + /// + public override string Caption { + get { + if (_caption == null) { + _caption = CommonUtils.CreateFriendlyPath( + Path.GetDirectoryName(this.ProjectMgr.BaseURI.Uri.LocalPath), this.Url); + } + return _caption; + } + } + + /// + /// Disable inline editing of Caption of a Search Path Node + /// + public override string GetEditLabel() { + return null; + } + + public override object GetIconHandle(bool open) { + return this.ProjectMgr.ImageHandler.GetIconHandle( + CommonProjectNode.ImageOffset + (Directory.Exists(this.Url)? + (int)CommonImageName.SearchPath : + (int)CommonImageName.MissingSearchPath)); + } + + /// + /// Search path node cannot be dragged. + /// + protected internal override StringBuilder PrepareSelectedNodesForClipBoard() { + return null; + } + + /// + /// Search Path Node cannot be excluded. + /// + protected override int ExcludeFromProject() { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + /// + /// Disable Copy/Cut/Paste commands on Search Path node. + /// + protected override int QueryStatusOnNode(Guid cmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result) { + if (cmdGroup == VsMenus.guidStandardCommandSet97) { + switch ((VsCommands)cmd) { + case VsCommands.Copy: + case VsCommands.Cut: + case VsCommands.Paste: + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.INVISIBLE; + return VSConstants.S_OK; + } + } + return base.QueryStatusOnNode(cmdGroup, cmd, pCmdText, ref result); + } + + /// + /// Defines whether this node is valid node for painting the Search Path icon. + /// + /// + protected override bool CanShowDefaultIcon() { + return true; + } + + protected override NodeProperties CreatePropertiesObject() { + return new CommonSearchPathNodeProperties(this); + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonConfigProvider.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonConfigProvider.cs new file mode 100644 index 0000000000..6493988d68 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonConfigProvider.cs @@ -0,0 +1,66 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Project; + +namespace Microsoft.IronStudio.Project { + /// + /// Enables the Any CPU Platform form name for Dynamic Projects. + /// Hooks language specific project config. + /// + public class CommonConfigProvider : ConfigProvider { + private CommonProjectNode _project; + + #region ctors + public CommonConfigProvider(CommonProjectNode project) + : base(project) { + _project = project; + } + #endregion + + #region overridden methods + + protected override ProjectConfig CreateProjectConfiguration(string configName) { + return new CommonProjectConfig(_project, configName); + } + + public override int GetPlatformNames(uint celt, string[] names, uint[] actual) { + if (names != null) { + names[0] = "Any CPU"; + } + + if (actual != null) { + actual[0] = 1; + } + + return VSConstants.S_OK; + } + + public override int GetSupportedPlatformNames(uint celt, string[] names, uint[] actual) { + if (names != null) { + names[0] = "Any CPU"; + } + + if (actual != null) { + actual[0] = 1; + } + + return VSConstants.S_OK; + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonEditorFactory.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonEditorFactory.cs new file mode 100644 index 0000000000..81b051ef6a --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonEditorFactory.cs @@ -0,0 +1,282 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Globalization; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.Designer.Interfaces; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TextManager.Interop; +using ErrorHandler = Microsoft.VisualStudio.ErrorHandler; +using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; +using VSConstants = Microsoft.VisualStudio.VSConstants; + +namespace Microsoft.IronStudio.Project { + /// + /// Common factory for creating our editor + /// + public abstract class CommonEditorFactory : IVsEditorFactory { + private CommonProjectPackage _package; + private ServiceProvider _serviceProvider; + + #region ctors + public CommonEditorFactory(CommonProjectPackage package) { + _package = package; + } + #endregion + + #region IVsEditorFactory Members + + public virtual int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider psp) { + _serviceProvider = new ServiceProvider(psp); + return VSConstants.S_OK; + } + + public virtual object GetService(Type serviceType) { + return _serviceProvider.GetService(serviceType); + } + + // This method is called by the Environment (inside IVsUIShellOpenDocument:: + // OpenStandardEditor and OpenSpecificEditor) to map a LOGICAL view to a + // PHYSICAL view. A LOGICAL view identifies the purpose of the view that is + // desired (e.g. a view appropriate for Debugging [LOGVIEWID_Debugging], or a + // view appropriate for text view manipulation as by navigating to a find + // result [LOGVIEWID_TextView]). A PHYSICAL view identifies an actual type + // of view implementation that an IVsEditorFactory can create. + // + // NOTE: Physical views are identified by a string of your choice with the + // one constraint that the default/primary physical view for an editor + // *MUST* use a NULL string as its physical view name (*pbstrPhysicalView = NULL). + // + // NOTE: It is essential that the implementation of MapLogicalView properly + // validates that the LogicalView desired is actually supported by the editor. + // If an unsupported LogicalView is requested then E_NOTIMPL must be returned. + // + // NOTE: The special Logical Views supported by an Editor Factory must also + // be registered in the local registry hive. LOGVIEWID_Primary is implicitly + // supported by all editor types and does not need to be registered. + // For example, an editor that supports a ViewCode/ViewDesigner scenario + // might register something like the following: + // HKLM\Software\Microsoft\VisualStudio\9.0\Editors\ + // {...guidEditor...}\ + // LogicalViews\ + // {...LOGVIEWID_TextView...} = s '' + // {...LOGVIEWID_Code...} = s '' + // {...LOGVIEWID_Debugging...} = s '' + // {...LOGVIEWID_Designer...} = s 'Form' + // + public virtual int MapLogicalView(ref Guid logicalView, out string physicalView) { + // initialize out parameter + physicalView = null; + + bool isSupportedView = false; + // Determine the physical view + if (VSConstants.LOGVIEWID_Primary == logicalView || + VSConstants.LOGVIEWID_Debugging == logicalView || + VSConstants.LOGVIEWID_Code == logicalView || + VSConstants.LOGVIEWID_TextView == logicalView) { + // primary view uses NULL as pbstrPhysicalView + isSupportedView = true; + } else if (VSConstants.LOGVIEWID_Designer == logicalView) { + physicalView = "Design"; + isSupportedView = true; + } + + if (isSupportedView) + return VSConstants.S_OK; + else { + // E_NOTIMPL must be returned for any unrecognized rguidLogicalView values + return VSConstants.E_NOTIMPL; + } + } + + public virtual int Close() { + return VSConstants.S_OK; + } + + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + /// + public virtual int CreateEditorInstance( + uint createEditorFlags, + string documentMoniker, + string physicalView, + IVsHierarchy hierarchy, + uint itemid, + System.IntPtr docDataExisting, + out System.IntPtr docView, + out System.IntPtr docData, + out string editorCaption, + out Guid commandUIGuid, + out int createDocumentWindowFlags) { + // Initialize output parameters + docView = IntPtr.Zero; + docData = IntPtr.Zero; + commandUIGuid = this.GetType().GUID; + createDocumentWindowFlags = 0; + editorCaption = null; + + // Validate inputs + if ((createEditorFlags & (VSConstants.CEF_OPENFILE | VSConstants.CEF_SILENT)) == 0) { + return VSConstants.E_INVALIDARG; + } + + // Get a text buffer + IVsTextLines textLines = GetTextBuffer(docDataExisting); + + // Assign docData IntPtr to either existing docData or the new text buffer + if (docDataExisting != IntPtr.Zero) { + docData = docDataExisting; + Marshal.AddRef(docData); + } else { + docData = Marshal.GetIUnknownForObject(textLines); + } + + try { + docView = CreateDocumentView(physicalView, hierarchy, itemid, textLines, out editorCaption, out commandUIGuid); + } finally { + if (docView == IntPtr.Zero) { + if (docDataExisting != docData && docData != IntPtr.Zero) { + // Cleanup the instance of the docData that we have addref'ed + Marshal.Release(docData); + docData = IntPtr.Zero; + } + } + } + return VSConstants.S_OK; + } + + + #endregion + + #region Helper methods + private IVsTextLines GetTextBuffer(System.IntPtr docDataExisting) { + IVsTextLines textLines; + if (docDataExisting == IntPtr.Zero) { + // Create a new IVsTextLines buffer. + Type textLinesType = typeof(IVsTextLines); + Guid riid = textLinesType.GUID; + Guid clsid = typeof(VsTextBufferClass).GUID; + textLines = _package.CreateInstance(ref clsid, ref riid, textLinesType) as IVsTextLines; + + // set the buffer's site + ((IObjectWithSite)textLines).SetSite(_serviceProvider.GetService(typeof(IOleServiceProvider))); + } else { + // Use the existing text buffer + Object dataObject = Marshal.GetObjectForIUnknown(docDataExisting); + textLines = dataObject as IVsTextLines; + if (textLines == null) { + // Try get the text buffer from textbuffer provider + IVsTextBufferProvider textBufferProvider = dataObject as IVsTextBufferProvider; + if (textBufferProvider != null) { + textBufferProvider.GetTextBuffer(out textLines); + } + } + if (textLines == null) { + // Unknown docData type then, so we have to force VS to close the other editor. + ErrorHandler.ThrowOnFailure((int)VSConstants.VS_E_INCOMPATIBLEDOCDATA); + } + + } + return textLines; + } + + private IntPtr CreateDocumentView(string physicalView, IVsHierarchy hierarchy, uint itemid, IVsTextLines textLines, out string editorCaption, out Guid cmdUI) { + //Init out params + editorCaption = string.Empty; + cmdUI = Guid.Empty; + + if (string.IsNullOrEmpty(physicalView)) { + // create code window as default physical view + return CreateCodeView(textLines, ref editorCaption, ref cmdUI); + } else if (string.Compare(physicalView, "design", true, CultureInfo.InvariantCulture) == 0) { + // Create Form view + return CreateFormView(hierarchy, itemid, textLines, ref editorCaption, ref cmdUI); + } + + // We couldn't create the view + // Return special error code so VS can try another editor factory. + ErrorHandler.ThrowOnFailure((int)VSConstants.VS_E_UNSUPPORTEDFORMAT); + + return IntPtr.Zero; + } + + private IntPtr CreateFormView(IVsHierarchy hierarchy, uint itemid, IVsTextLines textLines, ref string editorCaption, ref Guid cmdUI) { + // Request the Designer Service + IVSMDDesignerService designerService = (IVSMDDesignerService)GetService(typeof(IVSMDDesignerService)); + + // Create loader for the designer + IVSMDDesignerLoader designerLoader = (IVSMDDesignerLoader)designerService.CreateDesignerLoader("Microsoft.VisualStudio.Designer.Serialization.VSDesignerLoader"); + + bool loaderInitalized = false; + try { + IOleServiceProvider provider = _serviceProvider.GetService(typeof(IOleServiceProvider)) as IOleServiceProvider; + // Initialize designer loader + designerLoader.Initialize(provider, hierarchy, (int)itemid, textLines); + loaderInitalized = true; + + // Create the designer + IVSMDDesigner designer = designerService.CreateDesigner(provider, designerLoader); + + // Get editor caption + editorCaption = designerLoader.GetEditorCaption((int)READONLYSTATUS.ROSTATUS_Unknown); + + // Get view from designer + object docView = designer.View; + + // Get command guid from designer + cmdUI = designer.CommandGuid; + + return Marshal.GetIUnknownForObject(docView); + + } catch { + // The designer loader may have created a reference to the shell or the text buffer. + // In case we fail to create the designer we should manually dispose the loader + // in order to release the references to the shell and the textbuffer + if (loaderInitalized) { + designerLoader.Dispose(); + } + throw; + } + } + + private IntPtr CreateCodeView(IVsTextLines textLines, ref string editorCaption, ref Guid cmdUI) { + Type codeWindowType = typeof(IVsCodeWindow); + Guid riid = codeWindowType.GUID; + Guid clsid = typeof(VsCodeWindowClass).GUID; + IVsCodeWindow window = (IVsCodeWindow)_package.CreateInstance(ref clsid, ref riid, codeWindowType); + ErrorHandler.ThrowOnFailure(window.SetBuffer(textLines)); + ErrorHandler.ThrowOnFailure(window.SetBaseEditorCaption(null)); + ErrorHandler.ThrowOnFailure(window.GetEditorCaption(READONLYSTATUS.ROSTATUS_Unknown, out editorCaption)); + cmdUI = VSConstants.GUID_TextEditorFactory; + return Marshal.GetIUnknownForObject(window); + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonFileNode.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonFileNode.cs new file mode 100644 index 0000000000..7af30b8e2e --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonFileNode.cs @@ -0,0 +1,315 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio.Project.Automation; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.TextManager.Interop; +using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants; +using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID; +using VSConstants = Microsoft.VisualStudio.VSConstants; + +namespace Microsoft.IronStudio.Project { + public class CommonFileNode : FileNode { + private OAVSProjectItem _vsProjectItem; + private CommonProjectNode _project; + + public CommonFileNode(CommonProjectNode root, ProjectElement e) + : base(root, e) { + _project = root; + } + + #region properties + /// + /// Returns bool indicating whether this node is of subtype "Form" + /// + public bool IsFormSubType { + get { + string result = this.ItemNode.GetMetadata(ProjectFileConstants.SubType); + if (!String.IsNullOrEmpty(result) && string.Compare(result, ProjectFileAttributeValue.Form, true, CultureInfo.InvariantCulture) == 0) + return true; + else + return false; + } + } + /// + /// Returns the SubType of a dynamic FileNode. It is + /// + public string SubType { + get { + return this.ItemNode.GetMetadata(ProjectFileConstants.SubType); + } + set { + this.ItemNode.SetMetadata(ProjectFileConstants.SubType, value); + } + } + + protected internal VSLangProj.VSProjectItem VSProjectItem { + get { + if (null == _vsProjectItem) { + _vsProjectItem = new OAVSProjectItem(this); + } + return _vsProjectItem; + } + } + #endregion + + #region overridden properties + + internal override object Object { + get { + return this.VSProjectItem; + } + } + #endregion + + #region overridden methods + + protected override NodeProperties CreatePropertiesObject() { + SingleFileGeneratorNodeProperties properties = new CommonFileNodeProperties(this); + properties.OnCustomToolChanged += new EventHandler(OnCustomToolChanged); + properties.OnCustomToolNameSpaceChanged += new EventHandler(OnCustomToolNameSpaceChanged); + return properties; + } + + public override int ImageIndex { + get { + if (IsFormSubType) { + return (int)ProjectNode.ImageName.WindowsForm; + } else if (this.FileName.ToLower().EndsWith(this._project.GetCodeFileExtension())) { + if (NativeMethods.IsSamePath(this.Url, _project.GetStartupFile())) { + return CommonProjectNode.ImageOffset + (int)CommonImageName.StartupFile; + } else { + return CommonProjectNode.ImageOffset + (int)CommonImageName.File; + } + } + return base.ImageIndex; + } + } + + /// + /// Open a file depending on the SubType property associated with the file item in the project file + /// + protected override void DoDefaultAction() { + FileDocumentManager manager = this.GetDocumentManager() as FileDocumentManager; + Debug.Assert(manager != null, "Could not get the FileDocumentManager"); + + Guid viewGuid = + (IsFormSubType ? VSConstants.LOGVIEWID_Designer : VSConstants.LOGVIEWID_Code); + IVsWindowFrame frame; + manager.Open(false, false, viewGuid, out frame, WindowFrameShowAction.Show); + } + + private static Guid CLSID_VsTextBuffer = new Guid("{8E7B96A8-E33D-11d0-A6D5-00C04FB67F6A}"); + + /// + /// Gets the text buffer for the file opening the document if necessary. + /// + public ITextBuffer GetTextBuffer() { + IVsTextManager textMgr = (IVsTextManager)GetService(typeof(SVsTextManager)); + var model = GetService(typeof(SComponentModel)) as IComponentModel; + var adapter = model.GetService(); + uint itemid; + + IVsRunningDocumentTable rdt = ProjectMgr.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + if (rdt != null) { + IVsHierarchy hier; + IVsPersistDocData persistDocData; + uint cookie; + bool docInRdt = true; + IntPtr docData = IntPtr.Zero; + int hr = NativeMethods.E_FAIL; + try { + //Getting a read lock on the document. Must be released later. + hr = rdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_ReadLock, GetMkDocument(), out hier, out itemid, out docData, out cookie); + if (ErrorHandler.Failed(hr) || docData == IntPtr.Zero) { + Guid iid = VSConstants.IID_IUnknown; + cookie = 0; + docInRdt = false; + ILocalRegistry localReg = this.ProjectMgr.GetService(typeof(SLocalRegistry)) as ILocalRegistry; + ErrorHandler.ThrowOnFailure(localReg.CreateInstance(CLSID_VsTextBuffer, null, ref iid, (uint)CLSCTX.CLSCTX_INPROC_SERVER, out docData)); + } + + persistDocData = Marshal.GetObjectForIUnknown(docData) as IVsPersistDocData; + } finally { + if (docData != IntPtr.Zero) { + Marshal.Release(docData); + } + } + + //Try to get the Text lines + IVsTextLines srpTextLines = persistDocData as IVsTextLines; + + if (srpTextLines == null) { + // Try getting a text buffer provider first + IVsTextBufferProvider srpTextBufferProvider = persistDocData as IVsTextBufferProvider; + if (srpTextBufferProvider != null) { + hr = srpTextBufferProvider.GetTextBuffer(out srpTextLines); + } + } + + // Unlock the document in the RDT if necessary + if (docInRdt && rdt != null) { + ErrorHandler.ThrowOnFailure(rdt.UnlockDocument((uint)(_VSRDTFLAGS.RDT_ReadLock | _VSRDTFLAGS.RDT_Unlock_NoSave), cookie)); + } + + if (srpTextLines != null) { + return adapter.GetDocumentBuffer(srpTextLines); + } + } + + IWpfTextView view = GetTextView(); + + return view.TextBuffer; + + } + + public IWpfTextView GetTextView() { + var model = GetService(typeof(SComponentModel)) as IComponentModel; + var adapter = model.GetService(); + + IVsTextView viewAdapter; + uint itemid; + IVsUIShellOpenDocument uiShellOpenDocument = GetService(typeof(SVsUIShellOpenDocument)) as IVsUIShellOpenDocument; + IVsUIHierarchy hierarchy; + IVsWindowFrame pWindowFrame; + + VsShellUtilities.OpenDocument( + ProjectMgr.Site, + this.GetMkDocument(), + Guid.Empty, + out hierarchy, + out itemid, + out pWindowFrame, + out viewAdapter); + + ErrorHandler.ThrowOnFailure(pWindowFrame.Show()); + return adapter.GetWpfTextView(viewAdapter); + } + + protected override int ExecCommandOnNode(Guid guidCmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { + Debug.Assert(this.ProjectMgr != null, "The Dynamic FileNode has no project manager"); + + if (this.ProjectMgr == null) { + throw new InvalidOperationException(); + } + if (guidCmdGroup == GuidList.guidIronStudioCmdSet) { + switch (cmd) { + case CommonConstants.SetAsStartupFileCmdId: + // Set the StartupFile project property to the Url of this node + this.ProjectMgr.SetProjectProperty(CommonConstants.StartupFile, this.Url); + break; + case CommonConstants.StartDebuggingCmdId: + case CommonConstants.StartWithoutDebuggingCmdId: + CommonProjectPackage package = (CommonProjectPackage)_project.Package; + IStarter starter = (IStarter)_project.GetService(typeof(IStarter)); + if (starter != null) { + starter.StartFile(_project, this.Url, + cmd == CommonConstants.StartDebuggingCmdId); + } + break; + } + return VSConstants.S_OK; + } + + return base.ExecCommandOnNode(guidCmdGroup, cmd, nCmdexecopt, pvaIn, pvaOut); + } + + /// + /// Handles the menuitems + /// + protected override int QueryStatusOnNode(Guid guidCmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result) { + if (guidCmdGroup == Microsoft.VisualStudio.Shell.VsMenus.guidStandardCommandSet2K) { + switch ((VsCommands2K)cmd) { + case VsCommands2K.EXCLUDEFROMPROJECT: + case VsCommands2K.RUNCUSTOMTOOL: + result |= QueryStatusResult.NOTSUPPORTED | QueryStatusResult.INVISIBLE; + return VSConstants.S_OK; + } + } else if (guidCmdGroup == GuidList.guidIronStudioCmdSet) { + if (this.ProjectMgr.IsCodeFile(this.Url)) { + switch (cmd) { + case CommonConstants.SetAsStartupFileCmdId: + //We enable "Set as StartUp File" command only on current language code files, + //the file is in project home dir and if the file is not the startup file already. + string startupFile = _project.GetStartupFile(); + if (IsInProjectHome() && + (string.IsNullOrEmpty(startupFile) || !NativeMethods.IsSamePath(startupFile, this.Url))) { + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + } + break; + case CommonConstants.StartDebuggingCmdId: + case CommonConstants.StartWithoutDebuggingCmdId: + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + break; + } + } + return VSConstants.S_OK; + } + return base.QueryStatusOnNode(guidCmdGroup, cmd, pCmdText, ref result); + } + + /// + /// Dynamic project don't support excluding files from a project. + /// + protected override int ExcludeFromProject() { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + /// + /// Common File Node can only be deleted from file system. + /// + protected override bool CanDeleteItem(__VSDELETEITEMOPERATION deleteOperation) { + return deleteOperation == __VSDELETEITEMOPERATION.DELITEMOP_DeleteFromStorage; + } + #endregion + + #region methods + + internal OleServiceProvider.ServiceCreatorCallback ServiceCreator { + get { return new OleServiceProvider.ServiceCreatorCallback(this.CreateServices); } + } + + protected virtual object CreateServices(Type serviceType) { + object service = null; + if (typeof(EnvDTE.ProjectItem) == serviceType) { + service = GetAutomationObject(); + } + return service; + } + + private bool IsInProjectHome() { + HierarchyNode parent = this.Parent; + while (parent != null) { + if (parent is CommonSearchPathNode) { + return false; + } + parent = parent.Parent; + } + return true; + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonFileNodeProperties.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonFileNodeProperties.cs new file mode 100644 index 0000000000..f45c9a579b --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonFileNodeProperties.cs @@ -0,0 +1,65 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.Project; + +namespace Microsoft.IronStudio.Project { + [ComVisible(true), CLSCompliant(false)] + [Guid(CommonConstants.FileNodePropertiesGuid)] + public class CommonFileNodeProperties : SingleFileGeneratorNodeProperties { + + public CommonFileNodeProperties(HierarchyNode node) + : base(node) { } + + #region properties + + //Hide Build Action property from the property inspector + [Browsable(false)] + public override BuildAction BuildAction { + get { + return base.BuildAction; + } + set { + base.BuildAction = value; + } + } + + //Hide Custom Tool property from the property inspector + [Browsable(false)] + public override string CustomTool { + get { + return base.CustomTool; + } + set { + base.CustomTool = value; + } + } + + //Hide Custom Tool Namespace property from the property inspector + [Browsable(false)] + public override string CustomToolNamespace { + get { + return base.CustomToolNamespace; + } + set { + base.CustomToolNamespace = value; + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonFolderNode.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonFolderNode.cs new file mode 100644 index 0000000000..b5e87c4e03 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonFolderNode.cs @@ -0,0 +1,76 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio.Shell.Interop; +using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants; +using VsCommands = Microsoft.VisualStudio.VSConstants.VSStd97CmdID; +using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID; +using VSConstants = Microsoft.VisualStudio.VSConstants; + +namespace Microsoft.IronStudio.Project { + + public class CommonFolderNode : FolderNode { + private CommonProjectNode _project; + + public CommonFolderNode(CommonProjectNode root, string path, ProjectElement element) + : base(root, path, element) { + _project = root; + } + + protected override int QueryStatusOnNode(Guid cmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result) { + //Hide Exclude from Project command, show everything else normal Folder node supports + if (cmdGroup == Microsoft.VisualStudio.Project.VsMenus.guidStandardCommandSet2K) { + if (cmd == CommonConstants.OpenFolderInExplorerCmdId) { + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + } + else if ((VsCommands2K)cmd == VsCommands2K.EXCLUDEFROMPROJECT) { + result |= QueryStatusResult.NOTSUPPORTED | QueryStatusResult.INVISIBLE; + return VSConstants.S_OK; + } + } + return base.QueryStatusOnNode(cmdGroup, cmd, pCmdText, ref result); + } + + protected override int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { + if (cmdGroup == Microsoft.VisualStudio.Project.VsMenus.guidStandardCommandSet2K) { + if (cmd == CommonConstants.OpenFolderInExplorerCmdId) { + Process.Start(this.VirtualNodeName); + return VSConstants.S_OK; + } + } + return base.ExecCommandOnNode(cmdGroup, cmd, nCmdexecopt, pvaIn, pvaOut); + } + + /// + /// Dynamic project don't support excluding files from a project. + /// + protected override int ExcludeFromProject() { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + /// + /// Common Folder Node can only be deleted from file system. + /// + protected override bool CanDeleteItem(__VSDELETEITEMOPERATION deleteOperation) { + return deleteOperation == __VSDELETEITEMOPERATION.DELITEMOP_DeleteFromStorage; + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonGeneralPropertyPage.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonGeneralPropertyPage.cs new file mode 100644 index 0000000000..beb99bfced --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonGeneralPropertyPage.cs @@ -0,0 +1,205 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing.Design; +using System.IO; +using System.Runtime.InteropServices; +using System.Windows.Forms.Design; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.IronStudio.Project { + [ComVisible(true)] + public abstract class CommonGeneralPropertyPage : SettingsPage, EnvDTE80.IInternalExtenderProvider { + private string _startupFile; + private string _searchPath, _interpreterPath; + private string _workingDirectory; + private string _commandLineArguments; + private bool _isWindowsApplication; + + public CommonGeneralPropertyPage() { + this.Name = DynamicProjectSR.GetString(DynamicProjectSR.GeneralCaption); + } + + #region overriden methods + + public override string GetClassName() { + return this.GetType().FullName; + } + + protected override void BindProperties() { + if (this.ProjectMgr == null) { + Debug.Assert(false); + return; + } + _startupFile = this.ProjectMgr.GetProjectProperty(CommonConstants.StartupFile, false); + _searchPath = this.ProjectMgr.GetProjectProperty(CommonConstants.SearchPath, false); + _interpreterPath = this.ProjectMgr.GetProjectProperty(CommonConstants.InterpreterPath, false); + _workingDirectory = this.ProjectMgr.GetProjectProperty(CommonConstants.WorkingDirectory, false); + //By default working directory is project directory + if (string.IsNullOrEmpty(_workingDirectory)) { + _workingDirectory = "."; + } + _commandLineArguments = this.ProjectMgr.GetProjectProperty(CommonConstants.CommandLineArguments, false); + _isWindowsApplication = Convert.ToBoolean(this.ProjectMgr.GetProjectProperty(CommonConstants.IsWindowsApplication, false)); + } + + protected override int ApplyChanges() { + if (this.ProjectMgr == null) { + Debug.Assert(false); + return VSConstants.E_INVALIDARG; + } + + this.ProjectMgr.SetProjectProperty(CommonConstants.StartupFile, _startupFile); + this.ProjectMgr.SetProjectProperty(CommonConstants.SearchPath, _searchPath); + this.ProjectMgr.SetProjectProperty(CommonConstants.InterpreterPath, _interpreterPath); + this.ProjectMgr.SetProjectProperty(CommonConstants.WorkingDirectory, _workingDirectory); + this.ProjectMgr.SetProjectProperty(CommonConstants.CommandLineArguments, _commandLineArguments); + this.ProjectMgr.SetProjectProperty(CommonConstants.IsWindowsApplication, _isWindowsApplication.ToString()); + this.IsDirty = false; + + return VSConstants.S_OK; + } + + #endregion + + #region exposed properties + + [SRCategoryAttribute(DynamicProjectSR.Application)] + [LocalizableDisplayName(DynamicProjectSR.StartupFile)] + [SRDescriptionAttribute(DynamicProjectSR.StartupFileDescription)] + [Editor(typeof(FileNameEditor), typeof(UITypeEditor))] + public string StartupFile { + get { return _startupFile; } + set { _startupFile = value; this.IsDirty = true; } + } + + [SRCategoryAttribute(DynamicProjectSR.Application)] + [LocalizableDisplayName(DynamicProjectSR.SearchPaths)] + [SRDescriptionAttribute(DynamicProjectSR.SearchPathsDescription)] + public string SearchPath { + get { return _searchPath; } + set { _searchPath = value; this.IsDirty = true; } + } + + [SRCategoryAttribute(DynamicProjectSR.Application)] + [LocalizableDisplayName(DynamicProjectSR.InterpreterPath)] + [SRDescriptionAttribute(DynamicProjectSR.InterpreterPathDescription)] + public string InterpreterPath { + get { return _interpreterPath; } + set { _interpreterPath = value; this.IsDirty = true; } + } + + [SRCategoryAttribute(DynamicProjectSR.Application)] + [LocalizableDisplayName(DynamicProjectSR.WorkingDirectory)] + [SRDescriptionAttribute(DynamicProjectSR.WorkingDirectoryDescription)] + [Editor(typeof(FolderNameEditor), typeof(UITypeEditor))] + public string WorkingDirectory { + get { return _workingDirectory; } + set { _workingDirectory = value; this.IsDirty = true; } + } + + [SRCategoryAttribute(DynamicProjectSR.Application)] + [LocalizableDisplayName(DynamicProjectSR.CommandLineArguments)] + [SRDescriptionAttribute(DynamicProjectSR.CommandLineArgumentsDescription)] + public string CommandLineArguments { + get { return _commandLineArguments; } + set { _commandLineArguments = value; this.IsDirty = true; } + } + + [SRCategoryAttribute(DynamicProjectSR.Project)] + [LocalizableDisplayName(DynamicProjectSR.ProjectFile)] + [SRDescriptionAttribute(DynamicProjectSR.ProjectFileDescription)] + [AutomationBrowsable(false)] + public string ProjectFile { + get { return Path.GetFileName(this.ProjectMgr.ProjectFile); } + } + + [SRCategoryAttribute(DynamicProjectSR.Project)] + [LocalizableDisplayName(DynamicProjectSR.ProjectFolder)] + [SRDescriptionAttribute(DynamicProjectSR.ProjectFolderDescription)] + [AutomationBrowsable(false)] + public string ProjectFolder { + get { return Path.GetDirectoryName(this.ProjectMgr.ProjectFolder); } + } + + [SRCategoryAttribute(DynamicProjectSR.Application)] + [LocalizableDisplayName(DynamicProjectSR.IsWindowsApplication)] + [SRDescriptionAttribute(DynamicProjectSR.IsWindowsApplicationDescription)] + public bool IsWindowsApplication { + get { return _isWindowsApplication; } + set { _isWindowsApplication = value; this.IsDirty = true; } + } + + + #endregion + + #region IInternalExtenderProvider Members + + bool EnvDTE80.IInternalExtenderProvider.CanExtend(string extenderCATID, string extenderName, object extendeeObject) { + IVsHierarchy outerHierarchy = HierarchyNode.GetOuterHierarchy(this.ProjectMgr); + if (outerHierarchy is EnvDTE80.IInternalExtenderProvider) + return ((EnvDTE80.IInternalExtenderProvider)outerHierarchy).CanExtend(extenderCATID, extenderName, extendeeObject); + return false; + } + + object EnvDTE80.IInternalExtenderProvider.GetExtender(string extenderCATID, string extenderName, object extendeeObject, EnvDTE.IExtenderSite extenderSite, int cookie) { + IVsHierarchy outerHierarchy = HierarchyNode.GetOuterHierarchy(this.ProjectMgr); + if (outerHierarchy is EnvDTE80.IInternalExtenderProvider) + return ((EnvDTE80.IInternalExtenderProvider)outerHierarchy).GetExtender(extenderCATID, extenderName, extendeeObject, extenderSite, cookie); + return null; + } + + object EnvDTE80.IInternalExtenderProvider.GetExtenderNames(string extenderCATID, object extendeeObject) { + IVsHierarchy outerHierarchy = HierarchyNode.GetOuterHierarchy(this.ProjectMgr); + if (outerHierarchy is EnvDTE80.IInternalExtenderProvider) + return ((EnvDTE80.IInternalExtenderProvider)outerHierarchy).GetExtenderNames(extenderCATID, extendeeObject); + return null; + } + + #endregion + + #region ExtenderSupport + + [Browsable(false)] + [AutomationBrowsable(false)] + public virtual string ExtenderCATID { + get { + Guid catid = this.ProjectMgr.ProjectMgr.GetCATIDForType(this.GetType()); + if (Guid.Empty.CompareTo(catid) == 0) + throw new NotImplementedException(); + return catid.ToString("B"); + } + } + + [Browsable(false)] + [AutomationBrowsable(false)] + public object ExtenderNames { + get { + EnvDTE.ObjectExtenders extenderService = (EnvDTE.ObjectExtenders)this.ProjectMgr.GetService(typeof(EnvDTE.ObjectExtenders)); + return extenderService.GetExtenderNames(this.ExtenderCATID, this); + } + } + public object get_Extender(string extenderName) { + EnvDTE.ObjectExtenders extenderService = (EnvDTE.ObjectExtenders)this.ProjectMgr.GetService(typeof(EnvDTE.ObjectExtenders)); + return extenderService.GetExtender(this.ExtenderCATID, extenderName, this); + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonNonCodeFileNode.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonNonCodeFileNode.cs new file mode 100644 index 0000000000..4b9dcb8ad3 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonNonCodeFileNode.cs @@ -0,0 +1,43 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio.Shell.Interop; +using System.Diagnostics; + +namespace Microsoft.IronStudio.Project { + public class CommonNonCodeFileNode : CommonFileNode { + public CommonNonCodeFileNode(CommonProjectNode root, ProjectElement e) + : base(root, e) { + } + + + /// + /// Open a file depending on the SubType property associated with the file item in the project file + /// + protected override void DoDefaultAction() { + FileDocumentManager manager = this.GetDocumentManager() as FileDocumentManager; + Debug.Assert(manager != null, "Could not get the FileDocumentManager"); + + Guid viewGuid = Guid.Empty; + IVsWindowFrame frame; + manager.Open(false, false, viewGuid, out frame, WindowFrameShowAction.Show); + } + + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonOutputGroup.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonOutputGroup.cs new file mode 100644 index 0000000000..af6a5194d0 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonOutputGroup.cs @@ -0,0 +1,80 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using System.Reflection; + +namespace Microsoft.IronStudio.Project { + class CommonOutputGroup : OutputGroup { + public CommonOutputGroup(string outputName, string msBuildTargetName, ProjectNode projectManager, ProjectConfig configuration) + : base(outputName, msBuildTargetName, projectManager, configuration) { + } + + public override int get_KeyOutput(out string pbstrCanonicalName) { + pbstrCanonicalName = "Foo"; + return VSConstants.S_OK; + } + + public override int get_KeyOutputObject(out IVsOutput2 ppKeyOutput) { + ppKeyOutput = new DummyOutput(); + return VSConstants.S_OK; + } + + class DummyOutput : IVsOutput2 { + #region IVsOutput2 Members + + public int get_CanonicalName(out string pbstrCanonicalName) { + pbstrCanonicalName = null; + return VSConstants.E_FAIL; + } + + public int get_DeploySourceURL(out string pbstrDeploySourceURL) { + pbstrDeploySourceURL = null; + return VSConstants.E_FAIL; + } + + public int get_DisplayName(out string pbstrDisplayName) { + pbstrDisplayName = null; + return VSConstants.E_FAIL; + } + + public int get_Property(string szProperty, out object pvar) { + if (szProperty == "FinalOutputPath") { + pvar = Assembly.GetExecutingAssembly().CodeBase; + return VSConstants.S_OK; + } + pvar = null; + return VSConstants.E_FAIL; + } + + public int get_RootRelativeURL(out string pbstrRelativePath) { + pbstrRelativePath = null; + return VSConstants.E_FAIL; + } + + public int get_Type(out Guid pguidType) { + pguidType = Guid.Empty; + return VSConstants.E_FAIL; + } + + #endregion + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonProjectConfig.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonProjectConfig.cs new file mode 100644 index 0000000000..118345df83 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonProjectConfig.cs @@ -0,0 +1,51 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.IronStudio.Project { + [CLSCompliant(false)] + [ComVisible(true)] + public class CommonProjectConfig : ProjectConfig { + private readonly CommonProjectNode/*!*/ _project; + + public CommonProjectConfig(CommonProjectNode/*!*/ project, string configuration) + : base(project, configuration) { + _project = project; + } + + public override int DebugLaunch(uint flags) { + IStarter starter = _project.Package.GetStarter(); + + __VSDBGLAUNCHFLAGS launchFlags = (__VSDBGLAUNCHFLAGS)flags; + if ((launchFlags & __VSDBGLAUNCHFLAGS.DBGLAUNCH_NoDebug) == __VSDBGLAUNCHFLAGS.DBGLAUNCH_NoDebug) { + //Start project with no debugger + starter.StartProject(_project, false); + } else { + //Start project with debugger + starter.StartProject(_project, true); + } + return VSConstants.S_OK; + } + + protected override OutputGroup CreateOutputGroup(ProjectNode project, KeyValuePair group) { + return new CommonOutputGroup(group.Key, group.Value, project, this); + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonProjectNode.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonProjectNode.cs new file mode 100644 index 0000000000..5a0c62f36a --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonProjectNode.cs @@ -0,0 +1,980 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Drawing; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Windows.Forms; + +// +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio.Project.Automation; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.Scripting.Utils; +using Microsoft.Windows.Design.Host; + +namespace Microsoft.IronStudio.Project { + using Microsoft.IronStudio.Navigation; + using VSConstants = Microsoft.VisualStudio.VSConstants; + + public enum CommonImageName { + File = 0, + Project = 1, + SearchPathContainer, + SearchPath, + MissingSearchPath, + StartupFile + } + + public abstract class CommonProjectNode : ProjectNode, IVsProjectSpecificEditorMap2, IVsDeferredSaveProject { + + #region abstract methods + + public abstract Type GetProjectFactoryType(); + public abstract Type GetEditorFactoryType(); + public abstract string GetProjectName(); + public abstract string GetCodeFileExtension(); + public virtual CommonFileNode CreateCodeFileNode(ProjectElement item) { + return new CommonFileNode(this, item); + } + public virtual CommonFileNode CreateNonCodeFileNode(ProjectElement item) { + return new CommonNonCodeFileNode(this, item); + } + public abstract string GetFormatList(); + public abstract Type GetGeneralPropertyPageType(); + public abstract Type GetLibraryManagerType(); + public abstract string GetProjectFileExtension(); + + #endregion + + #region fields + + private CommonProjectPackage/*!*/ _package; + private Guid _mruPageGuid = new Guid(CommonConstants.AddReferenceMRUPageGuid); + private VSLangProj.VSProject _vsProject = null; + private static ImageList _imageList; + private ProjectDocumentsListenerForStartupFileUpdates _projectDocListenerForStartupFileUpdates; + private static int _imageOffset; + private CommonSearchPathContainerNode _searchPathContainer; + private string _projectDir; + private bool _isRefreshing; + private object _automationObject; + + #endregion + + #region Properties + + public new CommonProjectPackage/*!*/ Package { + get { return _package; } + } + + public static int ImageOffset { + get { return _imageOffset; } + } + + /// + /// Get the VSProject corresponding to this project + /// + protected internal VSLangProj.VSProject VSProject { + get { + if (_vsProject == null) + _vsProject = new OAVSProject(this); + return _vsProject; + } + } + + private IVsHierarchy InteropSafeHierarchy { + get { + IntPtr unknownPtr = Utilities.QueryInterfaceIUnknown(this); + if (IntPtr.Zero == unknownPtr) { + return null; + } + IVsHierarchy hier = Marshal.GetObjectForIUnknown(unknownPtr) as IVsHierarchy; + return hier; + } + } + + /// + /// Returns project's directory name. + /// + public string ProjectDir { + get { return _projectDir; } + } + + /// + /// Indicates whether the project is currently is busy refreshing its hierarchy. + /// + public bool IsRefreshing { + get { return _isRefreshing; } + } + + /// + /// Language specific project images + /// + public static ImageList ImageList { + get { + return _imageList; + } + set { + _imageList = value; + } + } + #endregion + + #region ctor + + public CommonProjectNode(CommonProjectPackage/*!*/ package, ImageList/*!*/ imageList) { + ContractUtils.RequiresNotNull(package, "package"); + ContractUtils.RequiresNotNull(imageList, "imageList"); + + _package = package; + CanFileNodesHaveChilds = true; + OleServiceProvider.AddService(typeof(VSLangProj.VSProject), new OleServiceProvider.ServiceCreatorCallback(CreateServices), false); + SupportsProjectDesigner = true; + _imageList = imageList; + + //Store the number of images in ProjectNode so we know the offset of the language icons. + _imageOffset = ImageHandler.ImageList.Images.Count; + foreach (Image img in ImageList.Images) { + ImageHandler.AddImage(img); + } + + InitializeCATIDs(); + } + + #endregion + + #region overridden properties + + /// + /// Since we appended the language images to the base image list in the constructor, + /// this should be the offset in the ImageList of the langauge project icon. + /// + public override int ImageIndex { + get { + return _imageOffset + (int)CommonImageName.Project; + } + } + + public override Guid ProjectGuid { + get { + return GetProjectFactoryType().GUID; + } + } + public override string ProjectType { + get { + return GetProjectName(); + } + } + internal override object Object { + get { + return VSProject; + } + } + #endregion + + #region overridden methods + + public override object GetAutomationObject() { + if (_automationObject == null) { + _automationObject = base.GetAutomationObject(); + } + return _automationObject; + } + + protected override int QueryStatusOnNode(Guid cmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result) { + if (cmdGroup == CommonConstants.Std97CmdGroupGuid) { + switch ((VSConstants.VSStd97CmdID)cmd) { + case VSConstants.VSStd97CmdID.BuildCtx: + case VSConstants.VSStd97CmdID.RebuildCtx: + case VSConstants.VSStd97CmdID.CleanCtx: + result = QueryStatusResult.SUPPORTED | QueryStatusResult.INVISIBLE; + return VSConstants.S_OK; + } + } else if (cmdGroup == GuidList.guidIronStudioCmdSet) { + switch ((int)cmd) { + case CommonConstants.AddSearchPathCommandId: + case CommonConstants.StartWithoutDebuggingCmdId: + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + } + } + return base.QueryStatusOnNode(cmdGroup, cmd, pCmdText, ref result); + } + + protected override int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { + if (cmdGroup == GuidList.guidIronStudioCmdSet) { + switch ((int)cmd) { + case CommonConstants.AddSearchPathCommandId: + AddSearchPath(); + return VSConstants.S_OK; + case CommonConstants.StartWithoutDebuggingCmdId: + EnvDTE.Project automationObject = this.GetAutomationObject() as EnvDTE.Project; + string activeConfigName = Utilities.GetActiveConfigurationName(automationObject); + CommonProjectConfig config = new CommonProjectConfig(this, activeConfigName); + return config.DebugLaunch((uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_NoDebug); + } + } + return base.ExecCommandOnNode(cmdGroup, cmd, nCmdexecopt, pvaIn, pvaOut); + } + + /// + /// As we don't register files/folders in the project file, removing an item is a noop. + /// + public override int RemoveItem(uint reserved, uint itemId, out int result) { + result = 1; + return VSConstants.S_OK; + } + + //No build for dynamic languages by default! + public override MSBuildResult Build(uint vsopts, string config, IVsOutputWindowPane output, string target) { + return MSBuildResult.Successful; + } + + internal override void BuildAsync(uint vsopts, string config, IVsOutputWindowPane output, string target, Action uiThreadCallback) { + uiThreadCallback(MSBuildResult.Successful, target); + } + + /// + /// Overriding main project loading method to inject our hierarachy of nodes. + /// + protected override void Reload() { + _projectDir = Path.GetDirectoryName(this.BaseURI.Uri.LocalPath); + _searchPathContainer = new CommonSearchPathContainerNode(this); + this.AddChild(_searchPathContainer); + base.Reload(); + RefreshHierarchy(); + OnProjectPropertyChanged += new EventHandler(CommonProjectNode_OnProjectPropertyChanged); + } + + protected override ReferenceContainerNode CreateReferenceContainerNode() { + return new CommonReferenceContainerNode(this); + } + + public override int GetGuidProperty(int propid, out Guid guid) { + if ((__VSHPROPID)propid == __VSHPROPID.VSHPROPID_PreferredLanguageSID) { + guid = new Guid("{EFB9A1D6-EA71-4F38-9BA7-368C33FCE8DC}");// GetLanguageServiceType().GUID; + } else { + return base.GetGuidProperty(propid, out guid); + } + return VSConstants.S_OK; + } + + protected override bool IsItemTypeFileType(string type) { + if (!base.IsItemTypeFileType(type)) { + if (String.Compare(type, "Page", StringComparison.OrdinalIgnoreCase) == 0 + || String.Compare(type, "ApplicationDefinition", StringComparison.OrdinalIgnoreCase) == 0 + || String.Compare(type, "Resource", StringComparison.OrdinalIgnoreCase) == 0) { + return true; + } else { + return false; + } + } else { + //This is a well known item node type, so return true. + return true; + } + } + + protected override NodeProperties CreatePropertiesObject() { + return new CommonProjectNodeProperties(this); + } + + public override int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider site) { + base.SetSite(site); + + //Initialize a new object to track project document changes so that we can update the StartupFile Property accordingly + _projectDocListenerForStartupFileUpdates = new ProjectDocumentsListenerForStartupFileUpdates((ServiceProvider)Site, this); + _projectDocListenerForStartupFileUpdates.Init(); + + return VSConstants.S_OK; + } + + public override int Close() { + if (null != _projectDocListenerForStartupFileUpdates) { + _projectDocListenerForStartupFileUpdates.Dispose(); + _projectDocListenerForStartupFileUpdates = null; + } + if (null != Site) { + LibraryManager libraryManager = Site.GetService(GetLibraryManagerType()) as LibraryManager; + if (null != libraryManager) { + libraryManager.UnregisterHierarchy(InteropSafeHierarchy); + } + } + + return base.Close(); + } + + public override void Load(string filename, string location, string name, uint flags, ref Guid iidProject, out int canceled) { + base.Load(filename, location, name, flags, ref iidProject, out canceled); + LibraryManager libraryManager = Site.GetService(GetLibraryManagerType()) as LibraryManager; + if (null != libraryManager) { + libraryManager.RegisterHierarchy(InteropSafeHierarchy); + } + + //If this is a WPFFlavor-ed project, then add a project-level DesignerContext service to provide + //event handler generation (EventBindingProvider) for the XAML designer. + this.OleServiceProvider.AddService(typeof(DesignerContext), new OleServiceProvider.ServiceCreatorCallback(this.CreateServices), false); + } + + /// + /// Overriding to provide project general property page + /// + /// + protected override Guid[] GetConfigurationIndependentPropertyPages() { + Guid[] result = new Guid[1]; + result[0] = GetGeneralPropertyPageType().GUID; + return result; + } + + /// + /// Overriding to provide customization of files on add files. + /// This will replace tokens in the file with actual value (namespace, class name,...) + /// + /// Full path to template file + /// Full path to destination file + public override void AddFileFromTemplate(string source, string target) { + if (!System.IO.File.Exists(source)) + throw new FileNotFoundException(String.Format("Template file not found: {0}", source)); + + // We assume that there is no token inside the file because the only + // way to add a new element should be through the template wizard that + // take care of expanding and replacing the tokens. + // The only task to perform is to copy the source file in the + // target location. + string targetFolder = Path.GetDirectoryName(target); + if (!Directory.Exists(targetFolder)) { + Directory.CreateDirectory(targetFolder); + } + + File.Copy(source, target); + } + + /// + /// Evaluates if a file is a current language code file based on is extension + /// + /// The filename to be evaluated + /// true if is a code file + public override bool IsCodeFile(string strFileName) { + // We do not want to assert here, just return silently. + if (String.IsNullOrEmpty(strFileName)) { + return false; + } + return (String.Compare(Path.GetExtension(strFileName), + GetCodeFileExtension(), + StringComparison.OrdinalIgnoreCase) == 0); + } + + /// + /// Create a file node based on an msbuild item. + /// + /// The msbuild item to be analyzed + public override FileNode CreateFileNode(ProjectElement item) { + if (item == null) { + throw new ArgumentNullException("item"); + } + + CommonFileNode newNode; + if (string.Compare(GetCodeFileExtension(), Path.GetExtension(item.GetFullPathForElement()), StringComparison.OrdinalIgnoreCase) == 0) { + newNode = CreateCodeFileNode(item); + } else { + newNode = CreateNonCodeFileNode(item); + } + string include = item.GetMetadata(ProjectFileConstants.Include); + + newNode.OleServiceProvider.AddService(typeof(EnvDTE.Project), + new OleServiceProvider.ServiceCreatorCallback(CreateServices), false); + newNode.OleServiceProvider.AddService(typeof(EnvDTE.ProjectItem), newNode.ServiceCreator, false); + if (!string.IsNullOrEmpty(include) && Path.GetExtension(include).Equals(".xaml", StringComparison.OrdinalIgnoreCase)) { + //Create a DesignerContext for the XAML designer for this file + newNode.OleServiceProvider.AddService(typeof(DesignerContext), newNode.ServiceCreator, false); + } + + newNode.OleServiceProvider.AddService(typeof(VSLangProj.VSProject), + new OleServiceProvider.ServiceCreatorCallback(CreateServices), false); + return newNode; + } + + /// + /// Create a file node based on absolute file name. + /// + public override FileNode CreateFileNode(string absFileName) { + // Avoid adding files to the project multiple times. Ultimately + // we should not use project items and instead should have virtual items. + string path = absFileName; + if (absFileName.Length > ProjectDir.Length && + String.Compare(ProjectDir, 0, absFileName, 0, ProjectDir.Length, StringComparison.OrdinalIgnoreCase) == 0) { + path = absFileName.Substring(ProjectDir.Length); + if (path.StartsWith("\\")) { + path = path.Substring(1); + } + } + var prjItem = GetExistingItem(absFileName) ?? BuildProject.AddItem("None", path)[0]; + ProjectElement prjElem = new ProjectElement(this, prjItem, false); + return CreateFileNode(prjElem); + } + + protected Microsoft.Build.Evaluation.ProjectItem GetExistingItem(string absFileName) { + Microsoft.Build.Evaluation.ProjectItem prjItem = null; + foreach (var item in BuildProject.Items) { + if (item.UnevaluatedInclude == absFileName) { + prjItem = item; + break; + } + } + return prjItem; + } + + public ProjectElement MakeProjectElement(string type, string path) { + var item = BuildProject.AddItem(type, path)[0]; + return new ProjectElement(this, item, false); + } + + public override int IsDirty(out int isDirty) { + isDirty = 0; + if (IsProjectFileDirty) { + isDirty = 1; + return VSConstants.S_OK; + } + + isDirty = IsFlavorDirty(); + return VSConstants.S_OK; + } + + protected override void AddNewFileNodeToHierarchy(HierarchyNode parentNode, string fileName) { + base.AddNewFileNodeToHierarchy(parentNode, fileName); + + SetProjectFileDirty(true); + } + + public override DependentFileNode CreateDependentFileNode(ProjectElement item) { + DependentFileNode node = base.CreateDependentFileNode(item); + if (null != node) { + string include = item.GetMetadata(ProjectFileConstants.Include); + if (IsCodeFile(include)) { + node.OleServiceProvider.AddService( + typeof(SVSMDCodeDomProvider), new OleServiceProvider.ServiceCreatorCallback(CreateServices), false); + } + } + + return node; + } + + /// + /// Creates the format list for the open file dialog + /// + /// The formatlist to return + /// Success + public override int GetFormatList(out string formatlist) { + formatlist = GetFormatList(); + return VSConstants.S_OK; + } + + /// + /// This overrides the base class method to show the VS 2005 style Add reference dialog. The ProjectNode implementation + /// shows the VS 2003 style Add Reference dialog. + /// + /// S_OK if succeeded. Failure other wise + public override int AddProjectReference() { + IVsComponentSelectorDlg2 componentDialog; + Guid guidEmpty = Guid.Empty; + VSCOMPONENTSELECTORTABINIT[] tabInit = new VSCOMPONENTSELECTORTABINIT[1]; + string strBrowseLocations = Path.GetDirectoryName(BaseURI.Uri.LocalPath); + + //Add the Project page + tabInit[0].dwSize = (uint)Marshal.SizeOf(typeof(VSCOMPONENTSELECTORTABINIT)); + // Tell the Add Reference dialog to call hierarchies GetProperty with the following + // propID to enable filtering out ourself from the Project to Project reference + tabInit[0].varTabInitInfo = (int)__VSHPROPID.VSHPROPID_ShowProjInSolutionPage; + tabInit[0].guidTab = VSConstants.GUID_SolutionPage; + + uint pX = 0, pY = 0; + + componentDialog = GetService(typeof(SVsComponentSelectorDlg)) as IVsComponentSelectorDlg2; + try { + // call the container to open the add reference dialog. + if (componentDialog != null) { + // Let the project know not to show itself in the Add Project Reference Dialog page + ShowProjectInSolutionPage = false; + + // call the container to open the add reference dialog. + ErrorHandler.ThrowOnFailure(componentDialog.ComponentSelectorDlg2( + (System.UInt32)(__VSCOMPSELFLAGS.VSCOMSEL_MultiSelectMode | __VSCOMPSELFLAGS.VSCOMSEL_IgnoreMachineName), + (IVsComponentUser)this, + 0, + null, + DynamicProjectSR.GetString(Microsoft.VisualStudio.Project.SR.AddReferenceDialogTitle), // Title + "VS.AddReference", // Help topic + ref pX, + ref pY, + (uint)tabInit.Length, + tabInit, + ref guidEmpty, + "*.dll", + ref strBrowseLocations)); + } + } catch (COMException e) { + Trace.WriteLine("Exception : " + e.Message); + return e.ErrorCode; + } finally { + // Let the project know it can show itself in the Add Project Reference Dialog page + ShowProjectInSolutionPage = true; + } + return VSConstants.S_OK; + } + + protected override ConfigProvider CreateConfigProvider() { + return new CommonConfigProvider(this); + } + #endregion + + #region Methods + + /// + /// Main method for refreshing project hierarchy. It's called on project loading + /// and each time the project property is changing. + /// + protected void RefreshHierarchy() { + try { + _isRefreshing = true; + string projHome = GetProjectHomeDir(); + string workDir = GetWorkingDirectory(); + IList searchPath = ParseSearchPath(); + + //Refresh CWD node + bool needCWD = !CommonUtils.AreTheSameDirectories(projHome, workDir); + var cwdNode = FindImmediateChild(_searchPathContainer); + if (needCWD) { + if (cwdNode == null) { + //No cwd node yet + _searchPathContainer.AddChild(new CurrentWorkingDirectoryNode(this, workDir)); + } else if (!CommonUtils.AreTheSameDirectories(cwdNode.Url, workDir)) { + //CWD has changed, recreate the node + cwdNode.Remove(false); + _searchPathContainer.AddChild(new CurrentWorkingDirectoryNode(this, workDir)); + } + } else { + //No need to show CWD, remove if exists + if (cwdNode != null) { + cwdNode.Remove(false); + } + } + + //Refresh regular search path nodes + + //We need to update search path nodes according to the search path property. + //It's quite expensive to remove all and build all nodes from scratch, + //so we are going to perform some smarter update. + //We are looping over paths in the search path and if a corresponding node + //exists, we only update its index (sort order), creating new node otherwise. + //At the end all nodes that haven't been updated have to be removed - they are + //not in the search path anymore. + var searchPathNodes = new List(); + this.FindNodesOfType(searchPathNodes); + bool[] updatedNodes = new bool[searchPathNodes.Count]; + int index; + for (int i = 0; i < searchPath.Count; i++) { + string path = searchPath[i]; + //ParseSearchPath() must resolve all paths + Debug.Assert(Path.IsPathRooted(path)); + var node = FindSearchPathNodeByPath(searchPathNodes, path, out index); + bool alreadyShown = CommonUtils.AreTheSameDirectories(workDir, path) || + CommonUtils.AreTheSameDirectories(projHome, path); + if (!alreadyShown) { + if (node != null) { + //existing path, update index (sort order) + node.Index = i; + updatedNodes[index] = true; + } else { + //new path - create new node + _searchPathContainer.AddChild(new CommonSearchPathNode(this, path, i)); + } + } + } + //Refresh nodes and remove non-updated ones + for (int i = 0; i < searchPathNodes.Count; i++) { + if (!updatedNodes[i]) { + searchPathNodes[i].Remove(); + } + } + // TODO: Port, fix me + //_searchPathContainer.UpdateSortOrder(); + _searchPathContainer.OnInvalidateItems(this); + } finally { + _isRefreshing = false; + } + } + + /// + /// Returns resolved value of the current working directory property. + /// + public string GetWorkingDirectory() { + string workDir = this.ProjectMgr.GetProjectProperty(CommonConstants.WorkingDirectory, true); + if (string.IsNullOrEmpty(workDir)) { + //If empty - take project directory as working directory + workDir = _projectDir; + } else if (!Path.IsPathRooted(workDir)) { + //If relative path - resolve it based on project home + workDir = Path.Combine(_projectDir, workDir); + } + return CommonUtils.NormalizeDirectoryPath(workDir); + } + + /// + /// Returns resolved value of the project home directory property. + /// + internal string GetProjectHomeDir() { + string projHome = this.ProjectMgr.GetProjectProperty(CommonConstants.ProjectHome, true); + if (string.IsNullOrEmpty(projHome)) { + //If empty - take project directory as project home + projHome = _projectDir; + } else if (!Path.IsPathRooted(projHome)) { + //If relative path - resolve it based on project directory + projHome = Path.Combine(_projectDir, projHome); + } + return CommonUtils.NormalizeDirectoryPath(projHome); + } + + /// + /// Returns resolved value of the startup file property. + /// + internal string GetStartupFile() { + string startupFile = ProjectMgr.GetProjectProperty(CommonConstants.StartupFile, true); + if (string.IsNullOrEmpty(startupFile)) { + //No startup file is assigned + return null; + } else if (!Path.IsPathRooted(startupFile)) { + //If relative path - resolve it based on project home + return Path.Combine(GetProjectHomeDir(), startupFile); + } + return startupFile; + } + + /// + /// Whenever project property has changed - refresh project hierarachy. + /// + private void CommonProjectNode_OnProjectPropertyChanged(object sender, ProjectPropertyChangedArgs e) { + RefreshHierarchy(); + } + + /// + /// Returns first immediate child node (non-recursive) of a given type. + /// + private static T FindImmediateChild(HierarchyNode parent) + where T : HierarchyNode { + for (HierarchyNode n = parent.FirstChild; n != null; n = n.NextSibling) { + if (n is T) { + return (T)n; + } + } + return null; + } + + /// + /// Finds Search Path node by a given search path and returns it along with the node's index. + /// + private CommonSearchPathNode FindSearchPathNodeByPath(IList nodes, string path, out int index) { + index = 0; + for (int j = 0; j < nodes.Count; j++) { + if (CommonUtils.AreTheSameDirectories(nodes[j].Url, path)) { + index = j; + return nodes[j]; + } + } + return null; + } + + /// + /// Provide mapping from our browse objects and automation objects to our CATIDs + /// + private void InitializeCATIDs() { + Type projectNodePropsType = typeof(CommonProjectNodeProperties); + Type fileNodePropsType = typeof(CommonFileNodeProperties); + // The following properties classes are specific to current language so we can use their GUIDs directly + AddCATIDMapping(projectNodePropsType, projectNodePropsType.GUID); + AddCATIDMapping(fileNodePropsType, fileNodePropsType.GUID); + // The following is not language specific and as such we need a separate GUID + AddCATIDMapping(typeof(FolderNodeProperties), new Guid(CommonConstants.FolderNodePropertiesGuid)); + // This one we use the same as language file nodes since both refer to files + AddCATIDMapping(typeof(FileNodeProperties), fileNodePropsType.GUID); + // Because our property page pass itself as the object to display in its grid, + // we need to make it have the same CATID + // as the browse object of the project node so that filtering is possible. + AddCATIDMapping(GetGeneralPropertyPageType(), projectNodePropsType.GUID); + // We could also provide CATIDs for references and the references container node, if we wanted to. + } + + /// + /// Parses SearchPath property into a list of distinct absolute paths, preserving the order. + /// + private IList ParseSearchPath() { + string searchPath = this.ProjectMgr.GetProjectProperty(CommonConstants.SearchPath, true); + List parsedPaths = new List(); + if (!string.IsNullOrEmpty(searchPath)) { + foreach (string path in searchPath.Split(new char[] { ';' }, StringSplitOptions.RemoveEmptyEntries)) { + string resolvedPath = CommonUtils.NormalizeDirectoryPath(Path.Combine(_projectDir, path)); + if (!parsedPaths.Contains(resolvedPath)) { + parsedPaths.Add(resolvedPath); + } + } + } + return parsedPaths; + } + + /// + /// Saves list of paths back as SearchPath project property. + /// + private void SaveSearchPath(IList value) { + string valueStr = ""; + if (value != null && value.Count > 0) { + valueStr = value.Aggregate((joined, path) => joined + ';' + path); + } + this.ProjectMgr.SetProjectProperty(CommonConstants.SearchPath, valueStr); + } + + /// + /// Adds new search path to the SearchPath project property. + /// + private void AddSearchPathEntry(string newpath) { + if (newpath == null) { + throw new ArgumentNullException("newpath"); + } + IList searchPath = ParseSearchPath(); + if (searchPath.Contains(newpath, StringComparer.CurrentCultureIgnoreCase)) { + return; + } + searchPath.Add(newpath); + SaveSearchPath(searchPath); + } + + /// + /// Removes a given path from the SearchPath property. + /// + internal void RemoveSearchPathEntry(string path) { + IList searchPath = ParseSearchPath(); + if (searchPath.Remove(path)) { + SaveSearchPath(searchPath); + } + } + + /// + /// Creates the services exposed by this project. + /// + private object CreateServices(Type serviceType) { + object service = null; + if (typeof(VSLangProj.VSProject) == serviceType) { + service = VSProject; + } else if (typeof(EnvDTE.Project) == serviceType) { + service = GetAutomationObject(); + } else if (typeof(DesignerContext) == serviceType) { + service = this.DesignerContext; + } + + return service; + } + + protected virtual internal Microsoft.Windows.Design.Host.DesignerContext DesignerContext { + get { + return null; + } + } + + /// + /// Executes Add Search Path menu command. + /// + internal int AddSearchPath() { + // Get a reference to the UIShell. + IVsUIShell uiShell = GetService(typeof(SVsUIShell)) as IVsUIShell; + if (null == uiShell) { + return VSConstants.S_FALSE; + } + //Create a fill in a structure that defines Browse for folder dialog + VSBROWSEINFOW[] browseInfo = new VSBROWSEINFOW[1]; + //Dialog title + browseInfo[0].pwzDlgTitle = DynamicProjectSR.GetString(DynamicProjectSR.SelectFolderForSearchPath); + //Initial directory - project directory + browseInfo[0].pwzInitialDir = _projectDir; + //Parent window + uiShell.GetDialogOwnerHwnd(out browseInfo[0].hwndOwner); + //Max path length + browseInfo[0].nMaxDirName = NativeMethods.MAX_PATH; + //This struct size + browseInfo[0].lStructSize = (uint)Marshal.SizeOf(typeof(VSBROWSEINFOW)); + //Memory to write selected directory to. + //Note: this one allocates unmanaged memory, which must be freed later + IntPtr pDirName = Marshal.AllocCoTaskMem(NativeMethods.MAX_PATH); + browseInfo[0].pwzDirName = pDirName; + try { + //Show the dialog + int hr = uiShell.GetDirectoryViaBrowseDlg(browseInfo); + if (hr == VSConstants.OLE_E_PROMPTSAVECANCELLED) { + //User cancelled the dialog + return VSConstants.S_OK; + } + //Check for any failures + ErrorHandler.ThrowOnFailure(hr); + //Get selected directory + string dirName = Marshal.PtrToStringAuto(browseInfo[0].pwzDirName); + AddSearchPathEntry(dirName); + } finally { + //Free allocated unmanaged memory + if (pDirName != IntPtr.Zero) { + Marshal.FreeCoTaskMem(pDirName); + } + } + return VSConstants.S_OK; + } + #endregion + + #region IVsProjectSpecificEditorMap2 Members + + public int GetSpecificEditorProperty(string mkDocument, int propid, out object result) { + // initialize output params + result = null; + + //Validate input + if (string.IsNullOrEmpty(mkDocument)) + throw new ArgumentException("Was null or empty", "mkDocument"); + + // Make sure that the document moniker passed to us is part of this project + // We also don't care if it is not a dynamic language file node + uint itemid; + ErrorHandler.ThrowOnFailure(ParseCanonicalName(mkDocument, out itemid)); + HierarchyNode hierNode = NodeFromItemId(itemid); + if (hierNode == null || ((hierNode as CommonFileNode) == null)) + return VSConstants.E_NOTIMPL; + + switch (propid) { + case (int)__VSPSEPROPID.VSPSEPROPID_UseGlobalEditorByDefault: + // we do not want to use global editor for form files + result = true; + break; + //case (int)__VSPSEPROPID.VSPSEPROPID_ProjectDefaultEditorName: + // result = "Python Form Editor"; + // break; + } + + return VSConstants.S_OK; + } + + public int GetSpecificEditorType(string mkDocument, out Guid guidEditorType) { + // Ideally we should at this point initalize a File extension to EditorFactory guid Map e.g. + // in the registry hive so that more editors can be added without changing this part of the + // code. Dynamic languages only make usage of one Editor Factory and therefore we will return + // that guid + guidEditorType = GetEditorFactoryType().GUID; + return VSConstants.S_OK; + } + + public int GetSpecificLanguageService(string mkDocument, out Guid guidLanguageService) { + guidLanguageService = Guid.Empty; + return VSConstants.E_NOTIMPL; + } + + public int SetSpecificEditorProperty(string mkDocument, int propid, object value) { + return VSConstants.E_NOTIMPL; + } + + #endregion + + #region IVsDeferredSaveProject Members + + /// + /// Implements deferred save support. Enabled by unchecking Tools->Options->Solutions and Projects->Save New Projects Created. + /// + /// In this mode we save the project when the user selects Save All. We need to move all the files in the project + /// over to the new location. + /// + public virtual int SaveProjectToLocation(string pszProjectFilename) { + string oldName = Url; + string basePath = Path.GetDirectoryName(this.FileName) + Path.DirectorySeparatorChar; + string newName = Path.GetDirectoryName(pszProjectFilename); + + // we don't use RenameProjectFile because it sends the OnAfterRenameProject event too soon + // and causes VS to think the solution has changed on disk. We need to send it after all + // updates are complete. + + // save the new project to to disk + SaveMSBuildProjectFileAs(pszProjectFilename); + + // remove all the children, saving any dirty files, and collecting the list of open files + MoveFilesForDeferredSave(this, basePath, newName); + + _projectDir = newName; + + // save the project again w/ updated file info + BuildProject.Save(); + + SetProjectFileDirty(false); + + // update VS that we've changed the project + this.OnPropertyChanged(this, (int)__VSHPROPID.VSHPROPID_Caption, 0); + + IVsUIShell shell = this.Site.GetService(typeof(SVsUIShell)) as IVsUIShell; + IVsSolution vsSolution = (IVsSolution)this.GetService(typeof(SVsSolution)); + // Update solution + ErrorHandler.ThrowOnFailure(vsSolution.OnAfterRenameProject((IVsProject)this, oldName, pszProjectFilename, 0)); + + ErrorHandler.ThrowOnFailure(shell.RefreshPropertyBrowser(0)); + + return VSConstants.S_OK; + } + + private static string GetNewFilePathForDeferredSave(string baseOldPath, string baseNewPath, string itemPath) { + var relativeName = itemPath.Substring(baseOldPath.Length); + return Path.Combine(baseNewPath, relativeName); + } + + private void MoveFilesForDeferredSave(HierarchyNode node, string basePath, string baseNewPath) { + if (node != null) { + for (var child = node.FirstChild; child != null; child = child.NextSibling) { + bool isOpen, isDirty, isOpenedByUs; + uint docCookie; + IVsPersistDocData persist; + var docMgr = child.GetDocumentManager(); + if (docMgr != null) { + docMgr.GetDocInfo(out isOpen, out isDirty, out isOpenedByUs, out docCookie, out persist); + int cancelled; + if (isDirty) { + child.SaveItem(VSSAVEFLAGS.VSSAVE_Save, null, docCookie, IntPtr.Zero, out cancelled); + } + + FileNode fn = child as FileNode; + if (fn != null) { + string newLoc = GetNewFilePathForDeferredSave(basePath, baseNewPath, child.Url); + + // make sure the directory is there + Directory.CreateDirectory(Path.GetDirectoryName(newLoc)); + fn.RenameDocument(child.Url, newLoc); + } + + FolderNode folder = child as FolderNode; + if (folder != null) { + folder.VirtualNodeName = GetNewFilePathForDeferredSave(basePath, baseNewPath, child.Url); + } + } + + MoveFilesForDeferredSave(child, basePath, baseNewPath); + } + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonProjectNodeProperties.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonProjectNodeProperties.cs new file mode 100644 index 0000000000..1a6f7cee02 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonProjectNodeProperties.cs @@ -0,0 +1,66 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.Project; + +namespace Microsoft.IronStudio.Project { + + [ComVisible(true)] + [CLSCompliant(false)] + [ClassInterface(ClassInterfaceType.AutoDual)] + [Guid(CommonConstants.ProjectNodePropertiesGuid)] + public class CommonProjectNodeProperties : ProjectNodeProperties { + + public CommonProjectNodeProperties(ProjectNode node) + : base(node) { + } + + #region properties + /// + /// Returns/Sets the StartupFile project property + /// + [Browsable(false)] + public string StartupFile { + get { + return this.Node.ProjectMgr.GetProjectProperty(CommonConstants.StartupFile, true); + } + } + + /// + /// Returns/Sets the WorkingDirectory project property + /// + [Browsable(false)] + public string SearchPath { + get { + return this.Node.ProjectMgr.GetProjectProperty(CommonConstants.SearchPath, true); + } + } + + //We don't need this property, but still have to provide it, otherwise + //Add New Item wizard (which seems to be unmanaged) fails. + [Browsable(false)] + public string RootNamespace { + get { + return ""; + } + set { + //Do nothing + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonProjectPackage.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonProjectPackage.cs new file mode 100644 index 0000000000..a306b52e41 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonProjectPackage.cs @@ -0,0 +1,158 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel.Design; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.Scripting.Utils; + +namespace Microsoft.IronStudio.Project { + //[DefaultRegistryRoot("Software\\Microsoft\\VisualStudio\\9.0Exp")] + //[PackageRegistration(UseManagedResourcesOnly = true)] + public abstract class CommonProjectPackage : ProjectPackage, IVsInstalledProduct { + private IStarter _starter; + + public abstract ProjectFactory CreateProjectFactory(); + public abstract CommonEditorFactory CreateEditorFactory(); + /// + /// This method is called to get the icon that will be displayed in the + /// Help About dialog when this package is selected. + /// + /// The resource id corresponding to the icon to display on the Help About dialog + public abstract uint GetIconIdForAboutBox(); + /// + /// This method is called during Devenv /Setup to get the bitmap to + /// display on the splash screen for this package. + /// + /// The resource id corresponding to the bitmap to display on the splash screen + public abstract uint GetIconIdForSplashScreen(); + /// + /// This methods provides the product official name, it will be + /// displayed in the help about dialog. + /// + public abstract string GetProductName(); + /// + /// This methods provides the product description, it will be + /// displayed in the help about dialog. + /// + public abstract string GetProductDescription(); + /// + /// This methods provides the product version, it will be + /// displayed in the help about dialog. + /// + public abstract string GetProductVersion(); + + /// + /// This method creates an instance of a service that + /// allows to start a project or a file with or without debugging. + /// + protected abstract IStarter/*!*/ CreateStarter(); + + protected override void Initialize() { + base.Initialize(); + this.RegisterProjectFactory(CreateProjectFactory()); + this.RegisterEditorFactory(CreateEditorFactory()); + } + + /// + /// This method retrieves an instance of a service that + /// allows to start a project or a file with or without debugging. + /// + public IStarter/*!*/ GetStarter() { + if (_starter == null) { + _starter = CreateStarter(); + ContractUtils.Requires(_starter != null, "GetStarter should return an instance of IStarter"); + } + return _starter; + } + + /// + /// This method loads a localized string based on the specified resource. + /// + /// Resource to load + /// String loaded for the specified resource + public string GetResourceString(string resourceName) { + string resourceValue; + IVsResourceManager resourceManager = (IVsResourceManager)GetService(typeof(SVsResourceManager)); + if (resourceManager == null) { + throw new InvalidOperationException("Could not get SVsResourceManager service. Make sure the package is Sited before calling this method"); + } + Guid packageGuid = this.GetType().GUID; + int hr = resourceManager.LoadResourceString(ref packageGuid, -1, resourceName, out resourceValue); + Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(hr); + return resourceValue; + } + + #region IVsInstalledProduct Members + + /// + /// This method is called during Devenv /Setup to get the bitmap to + /// display on the splash screen for this package. + /// + /// The resource id corresponding to the bitmap to display on the splash screen + /// HRESULT, indicating success or failure + public int IdBmpSplash(out uint pIdBmp) { + pIdBmp = GetIconIdForSplashScreen(); + return VSConstants.S_OK; + } + + /// + /// This method is called to get the icon that will be displayed in the + /// Help About dialog when this package is selected. + /// + /// The resource id corresponding to the icon to display on the Help About dialog + /// HRESULT, indicating success or failure + public int IdIcoLogoForAboutbox(out uint pIdIco) { + pIdIco = GetIconIdForAboutBox(); + return VSConstants.S_OK; + } + + /// + /// This methods provides the product official name, it will be + /// displayed in the help about dialog. + /// + /// Out parameter to which to assign the product name + /// HRESULT, indicating success or failure + public int OfficialName(out string pbstrName) { + pbstrName = GetProductName(); + return VSConstants.S_OK; + } + + /// + /// This methods provides the product description, it will be + /// displayed in the help about dialog. + /// + /// Out parameter to which to assign the description of the package + /// HRESULT, indicating success or failure + public int ProductDetails(out string pbstrProductDetails) { + pbstrProductDetails = GetProductDescription(); + return VSConstants.S_OK; + } + + /// + /// This methods provides the product version, it will be + /// displayed in the help about dialog. + /// + /// Out parameter to which to assign the version number + /// HRESULT, indicating success or failure + public int ProductID(out string pbstrPID) { + pbstrPID = GetProductVersion(); + return VSConstants.S_OK; + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonPropertyPage.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonPropertyPage.cs new file mode 100644 index 0000000000..0aa36920cb --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonPropertyPage.cs @@ -0,0 +1,173 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Drawing; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Project; + +namespace Microsoft.IronStudio.Project { + /// + /// Base class for property pages based on a WinForm control. + /// + public abstract class CommonPropertyPage : IPropertyPage { + private IPropertyPageSite _site; + private bool _dirty; + private ProjectNode _project; + + public abstract Control Control { + get; + } + + public abstract void Apply(); + public abstract void LoadSettings(); + + public abstract string Name { + get; + } + + public ProjectNode ProjectMgr { + get { + return _project; + } + } + + + public bool IsDirty { + get { + return _dirty; + } + set { + if (_dirty != value) { + _dirty = value; + if (_site != null) { + _site.OnStatusChange((uint)(_dirty ? PropPageStatus.Dirty : PropPageStatus.Clean)); + } + } + } + } + + void IPropertyPage.Activate(IntPtr hWndParent, RECT[] pRect, int bModal) { + NativeMethods.SetParent(Control.Handle, hWndParent); + } + + int IPropertyPage.Apply() { + try { + Apply(); + return VSConstants.S_OK; + } catch (Exception e) { + return Marshal.GetHRForException(e); + } + } + + void IPropertyPage.Deactivate() { + Control.Dispose(); + } + + void IPropertyPage.GetPageInfo(PROPPAGEINFO[] pPageInfo) { + if (pPageInfo == null) { + throw new ArgumentNullException("arrInfo"); + } + + PROPPAGEINFO info = new PROPPAGEINFO(); + + info.cb = (uint)Marshal.SizeOf(typeof(PROPPAGEINFO)); + info.dwHelpContext = 0; + info.pszDocString = null; + info.pszHelpFile = null; + info.pszTitle = Name; + info.SIZE.cx = Control.Width; + info.SIZE.cy = Control.Height; + pPageInfo[0] = info; + } + + void IPropertyPage.Help(string pszHelpDir) { + } + + int IPropertyPage.IsPageDirty() { + return (IsDirty ? (int)VSConstants.S_OK : (int)VSConstants.S_FALSE); + } + + void IPropertyPage.Move(RECT[] pRect) { + if (pRect == null) { + throw new ArgumentNullException("arrRect"); + } + + RECT r = pRect[0]; + + Control.Location = new Point(r.left, r.top); + Control.Size = new Size(r.right - r.left, r.bottom - r.top); + } + + void IPropertyPage.SetObjects(uint count, object[] punk) { + if (punk == null) { + return; + } + + if (count > 0) { + if (punk[0] is ProjectConfig) { + ArrayList configs = new ArrayList(); + + for (int i = 0; i < count; i++) { + ProjectConfig config = (ProjectConfig)punk[i]; + + if (_project == null) { + _project = config.ProjectMgr; + break; + } + + configs.Add(config); + } + } else if (punk[0] is NodeProperties) { + if (_project == null) { + _project = (punk[0] as NodeProperties).Node.ProjectMgr; + } + } + } else { + _project = null; + } + + if (_project != null) { + LoadSettings(); + } + } + + void IPropertyPage.SetPageSite(IPropertyPageSite pPageSite) { + _site = pPageSite; + } + + void IPropertyPage.Show(uint nCmdShow) { + Control.Visible = true; // TODO: pass SW_SHOW* flags through + Control.Show(); + } + + int IPropertyPage.TranslateAccelerator(MSG[] pMsg) { + if (pMsg == null) { + throw new ArgumentNullException("arrMsg"); + } + + MSG msg = pMsg[0]; + + if ((msg.message < NativeMethods.WM_KEYFIRST || msg.message > NativeMethods.WM_KEYLAST) && (msg.message < NativeMethods.WM_MOUSEFIRST || msg.message > NativeMethods.WM_MOUSELAST)) { + return VSConstants.S_FALSE; + } + + return (NativeMethods.IsDialogMessageA(Control.Handle, ref msg)) ? VSConstants.S_OK : VSConstants.S_FALSE; + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonReferenceContainerNode.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonReferenceContainerNode.cs new file mode 100644 index 0000000000..a1b5f79070 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonReferenceContainerNode.cs @@ -0,0 +1,54 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio.Shell.Interop; + + +namespace Microsoft.IronStudio.Project { + /// + /// Reference container node for project references. + /// + public class CommonReferenceContainerNode : ReferenceContainerNode { + public CommonReferenceContainerNode(ProjectNode project) + : base(project) { + } + + protected override ProjectReferenceNode CreateProjectReferenceNode(ProjectElement element) { + return new CommonReferenceNode(this.ProjectMgr, element); + } + + protected override ProjectReferenceNode CreateProjectReferenceNode(VSCOMPONENTSELECTORDATA selectorData) { + return new CommonReferenceNode(this.ProjectMgr, selectorData.bstrTitle, selectorData.bstrFile, selectorData.bstrProjRef); + } + + protected override NodeProperties CreatePropertiesObject() { + return new NodeProperties(this); + } + + /// + /// Creates a reference node. By default we don't add references and this returns null. + /// + protected override ReferenceNode CreateReferenceNode(VSCOMPONENTSELECTORDATA selectorData) { + return null; + } + + /// + /// Exposed for derived classes to re-enable reference support. + /// + protected ReferenceNode BaseCreateReferenceNode(ref VSCOMPONENTSELECTORDATA selectorData) { + return base.CreateReferenceNode(selectorData); + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonReferenceNode.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonReferenceNode.cs new file mode 100644 index 0000000000..f29fcaaa60 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonReferenceNode.cs @@ -0,0 +1,123 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.IronStudio.Project { + public class CommonReferenceNode : ProjectReferenceNode { + public CommonReferenceNode(ProjectNode root, ProjectElement element) + : base(root, element) { } + + public CommonReferenceNode(ProjectNode project, string referencedProjectName, string projectPath, string projectReference) + : base(project, referencedProjectName, projectPath, projectReference) { } + + /// + /// Gets a Project type string for a specified project instance guid + /// + /// The project type string + protected string GetProjectType() { + IVsHierarchy hierarchy = VsShellUtilities.GetHierarchy(this.ProjectMgr.Site, this.ReferencedProjectGuid); + object projectType; + ErrorHandler.ThrowOnFailure(hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_TypeName, out projectType)); + return projectType as string; + } + + /// + /// Evaluates all file node children of the project and returns true if anyone has subtype set to Form + /// + /// true if a file node with subtype Form is found + protected bool HasFormItems() { + if (!File.Exists(this.ReferencedProjectOutputPath)) { + List fileNodes = new List(); + this.ProjectMgr.FindNodesOfType(fileNodes); + foreach (CommonFileNode node in fileNodes) { + if (node.IsFormSubType) { + return true; + } + } + } + return false; + } + + /// + /// Checks if a reference can be added to the project. + /// It calls base to see if the reference is not already there, + /// and that it is not circular reference. + /// If the target project is a dynamic project too we can not add the project reference + /// because this scenario is not supported yet. + /// + /// The error handler delegate to return + /// false if reference cannot be added, otherwise true + protected override bool CanAddReference(out CannotAddReferenceErrorMessage errorHandler) { + //If the target project is a dynamic project too then show an error message + string referencedProjectType = GetProjectType(); + if (string.Compare(this.ProjectMgr.ProjectType, referencedProjectType, StringComparison.OrdinalIgnoreCase) == 0) { + errorHandler = new CannotAddReferenceErrorMessage(ShowProjectReferenceErrorMessage); + return false; + } + + //If source project has designer files of subtype form and if the target output (assembly) does not exists + //show a dialog that tells the user to build the target project before the project reference can be added + if (HasFormItems()) { + errorHandler = new CannotAddReferenceErrorMessage(ShowProjectReferenceErrorMessage2); + return false; + } + + //finally we must evaluate the the rules applied on the base class + if (!base.CanAddReference(out errorHandler)) { + return false; + } + + return true; + } + + /// + /// Shows Visual Studio message box with error message regarding project to project reference. + /// The message box is not show in case the method has been called from automation + /// + private void ShowProjectReferenceErrorMessage() { + if (!Utilities.IsInAutomationFunction(this.ProjectMgr.Site)) { + string message = DynamicProjectSR.GetString(DynamicProjectSR.ProjectReferenceError, CultureInfo.CurrentUICulture); + string title = string.Empty; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + VsShellUtilities.ShowMessageBox(this.ProjectMgr.Site, title, message, icon, buttons, defaultButton); + } + } + + /// + /// Shows Visual Studio message box with error message regarding project to project reference. Target Project must be built before + /// adding the the project to project reference. + /// The message box is not show in case the method has been called from automation + /// + private void ShowProjectReferenceErrorMessage2() { + if (!Utilities.IsInAutomationFunction(this.ProjectMgr.Site)) { + string message = DynamicProjectSR.GetString(DynamicProjectSR.ProjectReferenceError2, CultureInfo.CurrentUICulture); + string title = string.Empty; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + VsShellUtilities.ShowMessageBox(this.ProjectMgr.Site, title, message, icon, buttons, defaultButton); + } + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonSearchPathContainerNode.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonSearchPathContainerNode.cs new file mode 100644 index 0000000000..05baf361f2 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonSearchPathContainerNode.cs @@ -0,0 +1,146 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio.Shell.Interop; +using MSBuild = Microsoft.Build.BuildEngine; +using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants; + +namespace Microsoft.IronStudio.Project { + /// + /// Search path container node. + /// + [ComVisible(true)] + public class CommonSearchPathContainerNode : HierarchyNode { + internal const string SearchPathsNodeVirtualName = "Search Paths"; + private CommonProjectNode _projectNode; + + public CommonSearchPathContainerNode(CommonProjectNode project) + : base(project.ProjectMgr) { + _projectNode = project; + this.VirtualNodeName = SearchPathsNodeVirtualName; + this.ExcludeNodeFromScc = true; + } + + protected override NodeProperties CreatePropertiesObject() { + return new NodeProperties(this); + } + + /// + /// Gets the default sort priority of this node. + /// By default returns HierarchyNode. + /// + public override int SortPriority { + get { + return CommonConstants.SearchPathContainerNodeSortPriority; + } + } + + public override int MenuCommandId { + get { return VsMenus.IDM_VS_CTXT_ITEMNODE; } + } + + /// + /// Gets the GUID of the item type of the node. + /// By default returns System.Guid.Empty. + /// Implementations should return an item type GUID from the values listed in VSConstants. + /// + public override Guid ItemTypeGuid { + //A GUID constant used to specify that the type is a non-physical folder. + get { return VSConstants.GUID_ItemType_VirtualFolder; } + } + + /// + /// Gets the absolute path for this node. + /// + public override string Url { + get { return this.VirtualNodeName; } + } + + /// + /// Gets the caption of the hierarchy node. + /// + public override string Caption { + get { return DynamicProjectSR.GetString(DynamicProjectSR.SearchPaths); } + } + + /// + /// Disable inline editing of Caption of a SearchPathContainer Node + /// + public override string GetEditLabel() { + return null; + } + + public override object GetIconHandle(bool open) { + return this.ProjectMgr.ImageHandler.GetIconHandle( + CommonProjectNode.ImageOffset + (int)CommonImageName.SearchPathContainer); + } + + /// + /// Search path node cannot be dragged. + /// + protected internal override StringBuilder PrepareSelectedNodesForClipBoard() { + return null; + } + + /// + /// SearchPathContainer Node cannot be excluded. + /// + protected override int ExcludeFromProject() { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + protected override int QueryStatusOnNode(Guid cmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result) { + if (cmdGroup == GuidList.guidIronStudioCmdSet) { + switch (cmd) { + case CommonConstants.AddSearchPathCommandId: + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + } + } + + return base.QueryStatusOnNode(cmdGroup, cmd, pCmdText, ref result); + } + + protected override int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { + if (cmdGroup == GuidList.guidIronStudioCmdSet) { + switch (cmd) { + case CommonConstants.AddSearchPathCommandId: + return _projectNode.AddSearchPath(); + } + } + return base.ExecCommandOnNode(cmdGroup, cmd, nCmdexecopt, pvaIn, pvaOut); + } + + /// + /// SearchPathContainer Node cannot be deleted. + /// + protected override bool CanDeleteItem(__VSDELETEITEMOPERATION deleteOperation) { + return false; + } + + /// + /// Defines whether this node is valid node for painting the Search Paths icon. + /// + /// + protected override bool CanShowDefaultIcon() { + return true; + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonSearchPathNode.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonSearchPathNode.cs new file mode 100644 index 0000000000..2cfd61bff2 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonSearchPathNode.cs @@ -0,0 +1,87 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + + +using System; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio.Shell.Interop; +using VsCommands = Microsoft.VisualStudio.VSConstants.VSStd97CmdID; + +namespace Microsoft.IronStudio.Project { + /// + /// Represents seasrch path entry as a node in the Solution Explorer. + /// + [ComVisible(true)] + public class CommonSearchPathNode : BaseSearchPathNode { + private int _index; + public CommonSearchPathNode(CommonProjectNode project, string path, int index) + : base(project, path, project.MakeProjectElement("SearchPath", path)) { + _index = index; + } + + public override int SortPriority { + get { + return CommonConstants.SearchPathNodeMaxSortPriority + _index; + } + } + + public int Index { + get { return _index; } + set { _index = value; } + } + + protected override int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { + if (cmdGroup == VsMenus.guidStandardCommandSet97) { + switch ((VsCommands)cmd) { + case VsCommands.Delete: + case VsCommands.Remove: + string message = string.Format( + DynamicProjectSR.GetString(DynamicProjectSR.SearchPathRemoveConfirmation), + this.Caption); + string title = string.Empty; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_WARNING; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK | OLEMSGBUTTON.OLEMSGBUTTON_OKCANCEL; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + int res = Microsoft.VisualStudio.Shell.VsShellUtilities.ShowMessageBox(this.ProjectMgr.Site, title, message, icon, buttons, defaultButton); + if (res == 1) { + Remove(false); + } + return VSConstants.S_OK; + } + } + return base.ExecCommandOnNode(cmdGroup, cmd, nCmdexecopt, pvaIn, pvaOut); + } + + /// + /// Generic Search Path Node can only be removed from project. + /// + protected override bool CanDeleteItem(__VSDELETEITEMOPERATION deleteOperation) { + return deleteOperation == __VSDELETEITEMOPERATION.DELITEMOP_RemoveFromProject; + } + + public override void Remove(bool removeFromStorage) { + //Save this search path, because the node can be deleted after call to base Remove() + string path = this.VirtualNodeName; + base.Remove(removeFromStorage); + //Remove entry from project's Search Path + _project.RemoveSearchPathEntry(path); + } + + public void Remove() { + base.Remove(false); + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonSearchPathNodeProperties.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonSearchPathNodeProperties.cs new file mode 100644 index 0000000000..9ee415a2bc --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonSearchPathNodeProperties.cs @@ -0,0 +1,68 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.Project; + +namespace Microsoft.IronStudio.Project { + [ComVisible(true), CLSCompliant(false)] + [Guid(CommonConstants.SearchPathsPropertiesGuid)] + public class CommonSearchPathNodeProperties : NodeProperties { + #region properties + [SRCategoryAttribute(SR.Misc)] + [LocDisplayName(SR.FolderName)] + [SRDescriptionAttribute(SR.FolderNameDescription)] + [AutomationBrowsable(false)] + public string FolderName + { + get + { + return new DirectoryInfo(this.Node.Url).Name; + } + } + + [SRCategoryAttribute(SR.Misc)] + [LocDisplayName(SR.FullPath)] + [SRDescriptionAttribute(SR.FullPathDescription)] + [AutomationBrowsable(true)] + public string FullPath { + get { + return this.Node.VirtualNodeName; + } + } + + #region properties - used for automation only + [Browsable(false)] + [AutomationBrowsable(true)] + public string FileName + { + get + { + return this.Node.VirtualNodeName; + } + } + + #endregion + + #endregion + + #region ctors + public CommonSearchPathNodeProperties(HierarchyNode node) + : base(node) { } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CommonStarter.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonStarter.cs new file mode 100644 index 0000000000..3f5363af0e --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CommonStarter.cs @@ -0,0 +1,378 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Specialized; +using System.Diagnostics; +using System.IO; +using System.Linq; +using System.Net.NetworkInformation; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows.Forms; +using Microsoft.Scripting.Utils; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.Win32; + +namespace Microsoft.IronStudio.Project { + /// + /// Implements common functionality of starting a project or a file + /// with or without debugging. + /// + public abstract class CommonStarter : IStarter { + private readonly IServiceProvider/*!*/ _serviceProvider; + private static Process _chironProcess; + private static string _chironDir; + private static int _chironPort; + + public CommonStarter(IServiceProvider/*!*/ serviceProvider) { + ContractUtils.RequiresNotNull(serviceProvider, "serviceProvider"); + _serviceProvider = serviceProvider; + } + + #region IStarter Members + + public virtual void StartProject(CommonProjectNode project, bool debug) { + string startupFile = ResolveStartupFile(project); + StartFile(project, startupFile, debug); + } + + public virtual void StartFile(CommonProjectNode project, string/*!*/ file, bool debug) { + string extension = Path.GetExtension(file); + if (String.Equals(extension, ".html", StringComparison.OrdinalIgnoreCase) || + String.Equals(extension, ".htm", StringComparison.OrdinalIgnoreCase)) { + StartSilverlightApp(project, file, debug); + return; + } + + if (debug) { + StartWithDebugger(project, file); + } else { + StartWithoutDebugger(project, file); + } + } + + private static Guid guidSilvelightDebug = new Guid("{032F4B8C-7045-4B24-ACCF-D08C9DA108FE}"); + + public void StartSilverlightApp(CommonProjectNode project, string/*!*/ file, bool debug) { + string webSiteRoot; + if (project != null) { + webSiteRoot = project.GetWorkingDirectory(); + file = Path.GetFullPath(Path.Combine(webSiteRoot, file)); + } else { + file = Path.GetFullPath(file); + webSiteRoot = Path.GetDirectoryName(file); + } + webSiteRoot = webSiteRoot.TrimEnd('\\'); + + int port = EnsureChiron(webSiteRoot); + + string url = "http://localhost:" + port; + if (file.StartsWith(webSiteRoot) && file.Length > webSiteRoot.Length && file[webSiteRoot.Length] == '\\') { + url += file.Substring(webSiteRoot.Length).Replace('\\', '/'); + } else if (file.StartsWith("\\")) { + url += file.Replace('\\', '/'); + } else{ + url += '/' + file.Replace('\\', '/'); + } + + StartInBrowser(url, debug ? guidSilvelightDebug : (Guid?)null); + } + + public void StartInBrowser(string url, Guid? debugEngine) { + if (debugEngine.HasValue) { + // launch via VS debugger, it'll take care of figuring out the browsers + VsDebugTargetInfo dbgInfo = new VsDebugTargetInfo(); + dbgInfo.dlo = (DEBUG_LAUNCH_OPERATION)_DEBUG_LAUNCH_OPERATION3.DLO_LaunchBrowser; + dbgInfo.bstrExe = url; + dbgInfo.clsidCustom = debugEngine.Value; + dbgInfo.grfLaunch = (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_StopDebuggingOnEnd | (uint)__VSDBGLAUNCHFLAGS4.DBGLAUNCH_UseDefaultBrowser; + dbgInfo.cbSize = (uint)Marshal.SizeOf(dbgInfo); + VsShellUtilities.LaunchDebugger(_serviceProvider, dbgInfo); + } else { + // run the users default browser + var handler = GetBrowserHandlerProgId(); + var browserCmd = (string)Registry.ClassesRoot.OpenSubKey(handler).OpenSubKey("shell").OpenSubKey("open").OpenSubKey("command").GetValue(""); + + if (browserCmd.IndexOf("%1") != -1) { + browserCmd = browserCmd.Replace("%1", url); + } else { + browserCmd = browserCmd + " " + url; + } + bool inQuote = false; + string cmdLine = null; + for (int i = 0; i < browserCmd.Length; i++) { + if (browserCmd[i] == '"') { + inQuote = !inQuote; + } + + if (browserCmd[i] == ' ' && !inQuote) { + cmdLine = browserCmd.Substring(0, i); + break; + } + } + if (cmdLine == null) { + cmdLine = browserCmd; + } + + Process.Start(cmdLine, browserCmd.Substring(cmdLine.Length)); + } + } + + private static string GetBrowserHandlerProgId() { + try { + return (string)Registry.CurrentUser.OpenSubKey("Software").OpenSubKey("Microsoft").OpenSubKey("Windows").OpenSubKey("CurrentVersion").OpenSubKey("Explorer").OpenSubKey("FileExts").OpenSubKey(".html").OpenSubKey("UserChoice").GetValue("Progid"); + } catch { + return (string)Registry.ClassesRoot.OpenSubKey(".html").GetValue(""); + } + } + + public virtual string InstallPath { + get { + return Path.GetDirectoryName(typeof(CommonStarter).Assembly.Location); + } + } + + public virtual string ChironPath { + get { + return Path.Combine(Path.GetDirectoryName(typeof(CommonStarter).Assembly.Location), "Chiron.exe"); + } + } + + private int EnsureChiron(string/*!*/ webSiteRoot) { + Debug.Assert(!webSiteRoot.EndsWith("\\")); + + if (_chironDir != webSiteRoot && _chironProcess != null && !_chironProcess.HasExited) { + try { + _chironProcess.Kill(); + } catch { + // process already exited + } + _chironProcess = null; + } + + if (_chironProcess == null || _chironProcess.HasExited) { + // start Chiron + var chironPath = ChironPath; + // Get a free port + _chironPort = GetFreePort(); + + // TODO: race condition - the port might be taked by the time Chiron attempts to open it + // TODO: we should wait for Chiron before launching the browser + + string commandLine = "/w:" + _chironPort + " /notification /d:"; + + if (webSiteRoot.IndexOf(' ') != -1) { + commandLine += "\"" + webSiteRoot + "\""; + } else { + commandLine += webSiteRoot; + } + + ProcessStartInfo startInfo = new ProcessStartInfo(chironPath, commandLine); + startInfo.CreateNoWindow = true; + startInfo.UseShellExecute = false; + _chironDir = webSiteRoot; + _chironProcess = Process.Start(startInfo); + } + + return _chironPort; + } + + private static int GetFreePort() { + return Enumerable.Range(new Random().Next(1200, 2000), 60000).Except( + from connection in IPGlobalProperties.GetIPGlobalProperties().GetActiveTcpConnections() + select connection.LocalEndPoint.Port + ).First(); + } + + public virtual void StartFile(string/*!*/ file, bool debug) { + StartFile(null, file, debug); + } + + #endregion + + #region Abstracts + + /// + /// Returns full path of the language specififc iterpreter executable file. + /// + public abstract string/*!*/ GetInterpreterExecutable(CommonProjectNode project); + + private string/*!*/ GetInterpreterExecutableInternal(CommonProjectNode project) { + string result; + if (project != null) { + result = (project.ProjectMgr.GetProjectProperty(CommonConstants.InterpreterPath, true) ?? "").Trim(); + if (!String.IsNullOrEmpty(result)) { + if (!File.Exists(result)) { + throw new FileNotFoundException(String.Format("Interpreter specified in the project does not exist: '{0}'", result), result); + } + return result; + } + } + + result = GetInterpreterExecutable(project); + if (result == null) { + ContractUtils.RequiresNotNull(result, "result of GetInterpreterExecutable"); + } + return result; + } + + /// + /// Creates language specific command line for starting the project without debigging. + /// + public abstract string CreateCommandLineNoDebug(CommonProjectNode project, string startupFile); + /// + /// Creates language specific command line for starting the project with debigging. + /// + public abstract string CreateCommandLineDebug(CommonProjectNode project, string startupFile); + + #endregion + + #region Protected members + /// + /// Default implementation of the "Start withput Debugging" command. + /// + protected virtual void StartWithoutDebugger(CommonProjectNode project, string startupFile) { + Process.Start(CreateProcessStartInfoNoDebug(project, startupFile)); + } + + /// + /// Default implementation of the "Start Debugging" command. + /// + protected virtual void StartWithDebugger(CommonProjectNode project, string startupFile) { + VsDebugTargetInfo dbgInfo = new VsDebugTargetInfo(); + dbgInfo.cbSize = (uint)Marshal.SizeOf(dbgInfo); + IntPtr ptr = Marshal.AllocCoTaskMem((int)dbgInfo.cbSize); + try { + Marshal.StructureToPtr(dbgInfo, ptr, false); + SetupDebugInfo(ref dbgInfo, project, startupFile); + + LaunchDebugger(_serviceProvider, dbgInfo); + } finally { + if (ptr != IntPtr.Zero) { + Marshal.FreeCoTaskMem(ptr); + } + } + } + + private static void LaunchDebugger(IServiceProvider provider, VsDebugTargetInfo dbgInfo) { + if (!Directory.Exists(UnquotePath(dbgInfo.bstrCurDir))) { + MessageBox.Show(String.Format("Working directory \"{0}\" does not exist.", dbgInfo.bstrCurDir)); + } else if (!File.Exists(UnquotePath(dbgInfo.bstrExe))) { + MessageBox.Show(String.Format("Interpreter \"{0}\" does not exist.", dbgInfo.bstrExe)); + } else { + VsShellUtilities.LaunchDebugger(provider, dbgInfo); + } + } + + private static string UnquotePath(string p) { + if (p.StartsWith("\"") && p.EndsWith("\"")) { + return p.Substring(1, p.Length - 2); + } + return p; + } + + //TODO: this method should be protected, but due to IPy bug #19649 + //we keep it temporary public. + /// + /// Sets up debugger information. + /// + public virtual void SetupDebugInfo(ref VsDebugTargetInfo dbgInfo, + CommonProjectNode project, string startupFile) { + dbgInfo.dlo = DEBUG_LAUNCH_OPERATION.DLO_CreateProcess; + dbgInfo.bstrExe = GetInterpreterExecutableInternal(project); + dbgInfo.bstrCurDir = project != null ? + project.GetWorkingDirectory() : + Path.GetDirectoryName(startupFile); + dbgInfo.bstrArg = CreateCommandLineDebug(project, startupFile); + dbgInfo.bstrRemoteMachine = null; + dbgInfo.fSendStdoutToOutputWindow = 0; + StringDictionary env = new StringDictionary(); + + SetupEnvironment(project, env); + if (env.Count > 0) { + // add any inherited env vars + var variables = Environment.GetEnvironmentVariables(); + foreach (var key in variables.Keys) { + string strKey = (string)key; + if (!env.ContainsKey(strKey)) { + env.Add(strKey, (string)variables[key]); + } + } + + //Environemnt variables should be passed as a + //null-terminated block of null-terminated strings. + //Each string is in the following form:name=value\0 + StringBuilder buf = new StringBuilder(); + foreach (DictionaryEntry entry in env) { + buf.AppendFormat("{0}={1}\0", entry.Key, entry.Value); + } + buf.Append("\0"); + dbgInfo.bstrEnv = buf.ToString(); + } + //Set the managed debugger + dbgInfo.clsidCustom = VSConstants.CLSID_ComPlusOnlyDebugEngine; + dbgInfo.grfLaunch = (uint)__VSDBGLAUNCHFLAGS.DBGLAUNCH_StopDebuggingOnEnd; + } + + /// + /// Sets up environment variables before starting the project. + /// + protected virtual void SetupEnvironment(CommonProjectNode project, StringDictionary environment) { + //Do nothing by default + } + + /// + /// Creates process info used to start the project with no debugging. + /// + protected virtual ProcessStartInfo CreateProcessStartInfoNoDebug(CommonProjectNode project, string startupFile) { + string command = CreateCommandLineNoDebug(project, startupFile); + + string interpreter = GetInterpreterExecutableInternal(project); + command = "/c \"\"" + interpreter + "\" " + command + " & pause\""; + ProcessStartInfo startInfo = new ProcessStartInfo("cmd.exe", command); + + startInfo.WorkingDirectory = project != null ? + project.GetWorkingDirectory() : + Path.GetDirectoryName(startupFile); + + //In order to update environment variables we have to set UseShellExecute to false + startInfo.UseShellExecute = false; + SetupEnvironment(project, startInfo.EnvironmentVariables); + return startInfo; + } + + #endregion + + #region Private methods + + private string ResolveStartupFile(CommonProjectNode project) { + if (project == null) { + throw new ArgumentNullException("project"); + } + string startupFile = project.GetStartupFile(); + if (string.IsNullOrEmpty(startupFile)) { + //TODO: need to start active file then + throw new ApplicationException("No startup file is defined for the startup project."); + } + return startupFile; + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/CurrentWorkingDirectoryNode.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/CurrentWorkingDirectoryNode.cs new file mode 100644 index 0000000000..aebbc37b1a --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/CurrentWorkingDirectoryNode.cs @@ -0,0 +1,41 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +namespace Microsoft.IronStudio.Project { + /// + /// Represents Current Working Directory node. + /// + public class CurrentWorkingDirectoryNode : BaseSearchPathNode { + + public CurrentWorkingDirectoryNode(CommonProjectNode project, string path) + : base(project, path, project.MakeProjectElement("WorkingDirectory", path)) { } + + public override string Caption { + get { + return "Working Directory (" + base.Caption + ")"; + } + } + + //Working Directory node cannot be deleted + protected override bool CanDeleteItem(Microsoft.VisualStudio.Shell.Interop.__VSDELETEITEMOPERATION deleteOperation) { + return false; + } + + public override int SortPriority { + get { + return CommonConstants.WorkingDirectorySortPriority; + } + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/DesignerContext.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/DesignerContext.cs new file mode 100644 index 0000000000..5f282702bb --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/DesignerContext.cs @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/DirectoryBasedProjectNode.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/DirectoryBasedProjectNode.cs new file mode 100644 index 0000000000..501e6fd235 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/DirectoryBasedProjectNode.cs @@ -0,0 +1,249 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.IO; +using System.Threading; +using System.Windows.Threading; +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio.Shell.Interop; +using System.Windows.Forms; + +namespace Microsoft.IronStudio.Project { + public abstract class DirectoryBasedProjectNode : CommonProjectNode { + private FileSystemWatcher _projectWatcher; + + public DirectoryBasedProjectNode(CommonProjectPackage package, ImageList imageList) + : base(package, imageList) { + } + + protected internal override void ProcessFiles() { + //No files are registered in project file + } + + protected internal override void ProcessFolders() { + //No top-level folders in dynamic project + } + + protected override void Reload() { + base.Reload(); + + CreateHierarchy(this, GetProjectHomeDir(), false); + + InitializeFileSystemWatcher(); + } + + internal void CreateHierarchy(HierarchyNode parent, string dir, bool isSearchPath) { + string projFileExt = GetProjectFileExtension(); + if (Directory.Exists(dir)) { + try { + foreach (string subfolder in Directory.GetDirectories(dir)) { + AddDirectory(parent, isSearchPath, subfolder); + } + foreach (string file in Directory.GetFiles(dir)) { + if (ShouldIncludeFileInProject(projFileExt, file)) { + AddFile(parent, isSearchPath, file); + } + } + } catch (UnauthorizedAccessException) { + } + } + } + + /// + /// Adds a directory to the project hierarchy with the specified parent. + /// + protected void AddDirectory(HierarchyNode parent, bool isSearchPath, string subfolder) { + var existing = parent.FindChild(Path.GetFileName(subfolder)); + if (existing == null) { + FolderNode folderNode = CreateFolderNode(subfolder); + parent.AddChild(folderNode); + CreateHierarchy(folderNode, subfolder, isSearchPath); + } + } + + /// + /// Adds a file to the project hierarchy with the specified parent. + /// + /// + /// + /// + protected void AddFile(HierarchyNode parent, bool isSearchPath, string file) { + var existing = parent.FindChild(file); + if (existing == null) { + FileNode fileNode = CreateFileNode(file); + //Files in search path are not considered memebers of the project itself + fileNode.SetProperty((int)__VSHPROPID.VSHPROPID_IsNonMemberItem, isSearchPath); + parent.AddChild(fileNode); + } + } + + /// + /// Initializes the file system watcher so that we automatically track changes to the project system + /// based upon changes to the underlying file system. + /// + private void InitializeFileSystemWatcher() { + if (_projectWatcher != null) { + _projectWatcher.Created -= new FileSystemEventHandler(FileCreated); + _projectWatcher.Deleted -= new FileSystemEventHandler(FileDeleted); + } + + _projectWatcher = new FileSystemWatcher(ProjectDir); + _projectWatcher.IncludeSubdirectories = true; + _projectWatcher.SynchronizingObject = new SynchronizingInvoke(Dispatcher.CurrentDispatcher); + + _projectWatcher.Created += FileCreated; + _projectWatcher.Deleted += FileDeleted; + _projectWatcher.Renamed += FileRenamed; + + _projectWatcher.EnableRaisingEvents = true; + } + + private void FileDeleted(object sender, FileSystemEventArgs e) { + Debug.Assert(e.ChangeType == WatcherChangeTypes.Deleted); + + HierarchyNode child = FindChild(e.FullPath); + if (child != null) { + // TODO: We shouldn't be closing any documents, we probably need to pass a flag in here. + // Unfortunately it's not really simple because when we remove the child from the parent + // the file is no longer savable if it's already open. So for now the file just simply + // disappears - deleting it from the file system means you better want it gone from + // the editor as well. + child.Remove(false); + } + } + + private void FileRenamed(object sender, RenamedEventArgs e) { + Debug.Assert(e.ChangeType == WatcherChangeTypes.Renamed); + + var child = FindChild(e.OldFullPath); + if (child != null) { + FileNode fileNode = child as FileNode; + if (fileNode != null) { + // file nodes could be open so we'll need to update any + // state such as the file caption + try { + fileNode.RenameDocument(e.OldFullPath, e.FullPath); + } catch (Exception) { + } + } + + FolderNode folderNode = child as FolderNode; + if (folderNode != null) { + try { + folderNode.RenameFolder(e.FullPath); + } catch (Exception) { + } + } + + child.ItemNode.Rename(e.FullPath); + child.OnInvalidateItems(child.Parent); + } + } + + private void FileCreated(object sender, FileSystemEventArgs e) { + if (sender != _projectWatcher) { + // we've switched directories, ignore the old lingering events. + return; + } + + Debug.Assert(e.ChangeType == WatcherChangeTypes.Created); + + // find the parent where the new node will be inserted + HierarchyNode parent; + string path = e.FullPath; + for (; ; ) { + string dir = Path.GetDirectoryName(path); + if (NativeMethods.IsSamePath(dir, ProjectDir)) { + parent = this; + break; + } + + parent = FindChild(dir); + if (parent == null) { + path = dir; + } else { + break; + } + } + + // and then insert either a file or directory node + FileInfo fi = new FileInfo(e.FullPath); + if ((fi.Attributes & FileAttributes.Directory) != 0) { + AddDirectory(parent, false, path); + } else if (ShouldIncludeFileInProject(GetProjectFileExtension(), e.FullPath)) { + AddFile(parent, false, e.FullPath); + } + } + + /// + /// Checks if we should include the specified file in the project. + /// + /// Currently we only exclude project files for the current project type. + /// + protected static bool ShouldIncludeFileInProject(string projFileExt, string file) { + string extension = Path.GetExtension(file); + return + String.Compare(extension, projFileExt, StringComparison.OrdinalIgnoreCase) != 0 && + String.Compare(extension, ".sln", StringComparison.OrdinalIgnoreCase) != 0 && + (new FileInfo(file).Attributes & FileAttributes.Hidden) == 0; + } + + /// + /// Create a folder node based on absolute folder path. + /// + public new virtual FolderNode CreateFolderNode(string absFolderPath) { + //This code builds folder node in such a way that it won't be added to the + //project as build item and the project won't be go dirty. + var prjItem = GetExistingItem(absFolderPath) ?? BuildProject.AddItem("None", absFolderPath)[0]; + ProjectElement prjElem = new ProjectElement(this, prjItem, false); + return new CommonFolderNode(this, absFolderPath, prjElem); + } + + /// + /// Marshals any events fired by the file system watcher back onto the UI thread + /// where we can safely process the event. + /// + class SynchronizingInvoke : ISynchronizeInvoke { + private readonly Dispatcher _dispatcher; + + public SynchronizingInvoke(Dispatcher dispatcher) { + _dispatcher = dispatcher; + } + + #region ISynchronizeInvoke Members + + public IAsyncResult BeginInvoke(Delegate method, object[] args) { + _dispatcher.BeginInvoke(method, args); + return null; + } + + public object EndInvoke(IAsyncResult result) { + throw new NotImplementedException(); + } + + public object Invoke(Delegate method, object[] args) { + throw new NotImplementedException(); + } + + public bool InvokeRequired { + get { return Thread.CurrentThread != _dispatcher.Thread; } + } + + #endregion + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/IStarter.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/IStarter.cs new file mode 100644 index 0000000000..22785cff68 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/IStarter.cs @@ -0,0 +1,37 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +namespace Microsoft.IronStudio.Project { + /// + /// Defines an interface for starting a project or a file with + /// or without debugging. + /// + public interface IStarter { + /// + /// Starts a project with or without debugging. + /// + void StartProject(CommonProjectNode project, bool debug); + + /// + /// Starts a file in a project with or without debugging. + /// + void StartFile(CommonProjectNode project, string file, bool debug); + + /// + /// Starts standalone file with or without debugging. + /// Relative file paths are considered to be relative to the current directory. + /// + void StartFile(string/*!*/ file, bool debug); + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/ProjectDocumentsListenerForStartupFileUpdates.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/ProjectDocumentsListenerForStartupFileUpdates.cs new file mode 100644 index 0000000000..6cbde2f053 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/ProjectDocumentsListenerForStartupFileUpdates.cs @@ -0,0 +1,90 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.IO; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Project; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.IronStudio.Project { + /// + /// Updates the dynamic project property called StartupFile + /// + internal class ProjectDocumentsListenerForStartupFileUpdates : ProjectDocumentsListener { + #region fields + /// + /// The dynamic project who adviced for TrackProjectDocumentsEvents + /// + private CommonProjectNode _project; + #endregion + + #region ctors + public ProjectDocumentsListenerForStartupFileUpdates(ServiceProvider serviceProvider, CommonProjectNode project) + : base(serviceProvider) { + _project = project; + } + #endregion + + #region overriden methods + public override int OnAfterRenameFiles(int cProjects, int cFiles, IVsProject[] projects, int[] firstIndices, string[] oldFileNames, string[] newFileNames, VSRENAMEFILEFLAGS[] flags) { + if (!_project.IsRefreshing) { + //Get the current value of the StartupFile Property + string currentStartupFile = _project.GetProjectProperty(CommonConstants.StartupFile, true); + string fullPathToStartupFile = Path.Combine(_project.ProjectDir, currentStartupFile); + + //Investigate all of the oldFileNames if they are equal to the current StartupFile + int index = 0; + foreach (string oldfile in oldFileNames) { + //Compare the files and update the StartupFile Property if the currentStartupFile is an old file + if (NativeMethods.IsSamePath(oldfile, fullPathToStartupFile)) { + //Get the newfilename and update the StartupFile property + string newfilename = newFileNames[index]; + CommonFileNode node = _project.FindChild(newfilename) as CommonFileNode; + if (node == null) + throw new InvalidOperationException("Could not find the CommonFileNode object"); + //Startup file has been renamed + _project.SetProjectProperty(CommonConstants.StartupFile, node.Url); + break; + } + index++; + } + } + return VSConstants.S_OK; + } + + public override int OnAfterRemoveFiles(int cProjects, int cFiles, IVsProject[] projects, int[] firstIndices, string[] oldFileNames, VSREMOVEFILEFLAGS[] flags) { + if (!_project.IsRefreshing) { + //Get the current value of the StartupFile Property + string currentStartupFile = _project.GetProjectProperty(CommonConstants.StartupFile, true); + string fullPathToStartupFile = Path.Combine(_project.ProjectDir, currentStartupFile); + + //Investigate all of the oldFileNames if they are equal to the current StartupFile + int index = 0; + foreach (string oldfile in oldFileNames) { + //Compare the files and update the StartupFile Property if the currentStartupFile is an old file + if (NativeMethods.IsSamePath(oldfile, fullPathToStartupFile)) { + //Startup file has been removed + _project.SetProjectProperty(CommonConstants.StartupFile, null); + break; + } + index++; + } + } + return VSConstants.S_OK; + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/Resources.cs b/Tools/IronStudio/IronStudio/IronStudio/Project/Resources.cs new file mode 100644 index 0000000000..79cc13b7b9 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/Resources.cs @@ -0,0 +1,175 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Globalization; +using System.Resources; +using System.Threading; +using Microsoft.VisualStudio.Project; + +namespace Microsoft.IronStudio.Project { + /// + /// Specifies the localizable display name for a property, event, or public void method which takes no arguments. + /// First looks up the name in local string resources than falls back to MPFProj resources. + /// + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = false)] + public class LocalizableDisplayNameAttribute : DisplayNameAttribute { + private string _name; + + public LocalizableDisplayNameAttribute(string name) { + _name = name; + } + + public override string DisplayName { + get { + string result = DynamicProjectSR.GetString(_name); + if (result == null) { + result = SR.GetString(_name); + if (result == null) { + Debug.Assert(false, "String resource '" + _name + "' is missing"); + result = _name; + } + } + return result; + } + } + } + + [AttributeUsage(AttributeTargets.All)] + public sealed class SRDescriptionAttribute : DescriptionAttribute { + private bool _replaced = false; + + public SRDescriptionAttribute(string description) + : base(description) { + } + + public override string Description { + get { + if (!_replaced) { + _replaced = true; + DescriptionValue = DynamicProjectSR.GetString(base.Description); + } + return base.Description; + } + } + } + + [AttributeUsage(AttributeTargets.All)] + public sealed class SRCategoryAttribute : CategoryAttribute { + + public SRCategoryAttribute(string category) + : base(category) { + } + + protected override string GetLocalizedString(string value) { + return DynamicProjectSR.GetString(value); + } + } + + public class DynamicProjectSR { + public const string SearchPaths = "SearchPaths"; + public const string SearchPathsDescription = "SearchPathsDescription"; + public const string InterpreterPath = "InterpreterPath"; + public const string InterpreterPathDescription = "InterpreterPathDescription"; + public const string ProjectReferenceError = "ProjectReferenceError"; + public const string ProjectReferenceError2 = "ProjectReferenceError2"; + public const string Application = "Application"; + public const string GeneralCaption = "GeneralCaption"; + public const string Project = "Project"; + public const string ProjectFile = "ProjectFile"; + public const string ProjectFileDescription = "ProjectFileDescription"; + public const string ProjectFileExtensionFilter = "ProjectFileExtensionFilter"; + public const string ProjectFolder = "ProjectFolder"; + public const string ProjectFolderDescription = "ProjectFolderDescription"; + public const string StartupFile = "StartupFile"; + public const string StartupFileDescription = "StartupFileDescription"; + public const string SelectFolderForSearchPath = "SelectFolderForSearchPath"; + public const string SearchPathRemoveConfirmation = "SearchPathRemoveConfirmation"; + public const string WorkingDirectory = "WorkingDirectory"; + public const string WorkingDirectoryDescription = "WorkingDirectoryDescription"; + public const string CommandLineArguments = "CommandLineArguments"; + public const string CommandLineArgumentsDescription = "CommandLineArgumentsDescription"; + public const string IsWindowsApplication = "IsWindowsApplication"; + public const string IsWindowsApplicationDescription = "IsWindowsApplicationDescription"; + + private static DynamicProjectSR _loader = null; + private ResourceManager _resources; + private static Object _internalSyncObject; + + private static Object InternalSyncObject { + get { + if (_internalSyncObject == null) { + Object o = new Object(); + Interlocked.CompareExchange(ref _internalSyncObject, o, null); + } + return _internalSyncObject; + } + } + + internal DynamicProjectSR() { + _resources = new ResourceManager("Microsoft.IronStudio.Project.LocalizableDisplayNameAttribute", this.GetType().Assembly); + } + + private static DynamicProjectSR GetLoader() { + if (_loader == null) { + lock (InternalSyncObject) { + if (_loader == null) { + _loader = new DynamicProjectSR(); + } + } + } + + return _loader; + } + + private static CultureInfo Culture { + get { return null/*use ResourceManager default, CultureInfo.CurrentUICulture*/; } + } + + public static ResourceManager Resources { + get { + return GetLoader()._resources; + } + } + + public static string GetString(string name, params object[] args) { + DynamicProjectSR sys = GetLoader(); + if (sys == null) + return null; + string res = sys._resources.GetString(name, DynamicProjectSR.Culture); + + if (args != null && args.Length > 0) { + return String.Format(CultureInfo.CurrentCulture, res, args); + } else { + return res; + } + } + + public static string GetString(string name) { + DynamicProjectSR sys = GetLoader(); + if (sys == null) + return null; + return sys._resources.GetString(name, DynamicProjectSR.Culture); + } + + public static object GetObject(string name) { + DynamicProjectSR sys = GetLoader(); + if (sys == null) + return null; + return sys._resources.GetObject(name, DynamicProjectSR.Culture); + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/Resources.resx b/Tools/IronStudio/IronStudio/IronStudio/Project/Resources.resx new file mode 100644 index 0000000000..cbe22d97eb --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Project/Resources.resx @@ -0,0 +1,189 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Creating a Project Reference to a Dynamic Project is currently not supported. + + + You must build the target project before it can be referenced. + + + Application + + + General + + + Startup File + + + The name of the startup file that you want to be executed when you launch your application. + + + Project + + + Project File + + + The name of the file containing build, configuration, and other information about the project. + + + Project Folder + + + The absolute location of the project. + + + Search Path + + + The list of directories to search for modules and files. + + + Interpreter Path + + + The interpreter which is used to start the project. + + + Select a folder to add to the Search Path + + + '{0}' will be removed from the project's search path. + + + Working Directory + + + Working directory for debugging and executing. + + + Command Line Arguments + + + Command line arguments to be passed into the application on project start. + + + Is Windows Application + + + True if the application should start w/o a console window + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/IronStudio/Project/Resources/imagelis.bmp b/Tools/IronStudio/IronStudio/IronStudio/Project/Resources/imagelis.bmp new file mode 100644 index 0000000000..43cb71c75b Binary files /dev/null and b/Tools/IronStudio/IronStudio/IronStudio/Project/Resources/imagelis.bmp differ diff --git a/Tools/IronStudio/IronStudio/IronStudio/Repl/IReplWindowProvider.cs b/Tools/IronStudio/IronStudio/IronStudio/Repl/IReplWindowProvider.cs new file mode 100644 index 0000000000..07e1d4dd57 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Repl/IReplWindowProvider.cs @@ -0,0 +1,40 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronStudio.Repl { + /// + /// Provides REPL windows for all languages. + /// + public interface IReplWindowProvider { + /// + /// Creates a REPL window and returns a ToolWindowPane which implements IReplWindow. + /// + ToolWindowPane CreateReplWindow(IReplEvaluator/*!*/ evaluator, IContentType/*!*/ contentType, string/*!*/ title, Guid languageServiceGuid, Guid replId); + + ToolWindowPane CreateReplWindow(IReplEvaluator/*!*/ evaluator, IContentType/*!*/ contentType, int id, string/*!*/ title, Guid languageServiceGuid, Guid guid); + + /// + /// Finds the REPL w/ the specified ID or returns null if the window hasn't been created. + /// + ToolWindowPane FindReplWindow(Guid replId); + + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Repl/ReplWindowProvider.cs b/Tools/IronStudio/IronStudio/IronStudio/Repl/ReplWindowProvider.cs new file mode 100644 index 0000000000..0bdf487321 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Repl/ReplWindowProvider.cs @@ -0,0 +1,108 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronStudio.Repl { + [Export(typeof(IReplWindowProvider))] + internal sealed class ReplWindowProvider : IReplWindowProvider { + public ReplWindowProvider() { + } + + #region IReplWindowProvider Members + + public ToolWindowPane CreateReplWindow(IReplEvaluator/*!*/ evaluator, IContentType contentType, string/*!*/ title, Guid languageServiceGuid, Guid replGuid) { + int curId = 0; + + ToolWindowPane window; + do { + curId++; + window = FindReplWindow(curId); + } while (window != null); + + window = CreateReplWindow(evaluator, contentType, curId, title, languageServiceGuid, replGuid); + if ((null == window) || (null == window.Frame)) { + throw new NotSupportedException(Resources.CanNotCreateWindow); + } + + return window; + } + + private Dictionary _windows = new Dictionary(); + + public VsReplWindow FindReplWindow(int id) { + VsReplWindow res; + if (_windows.TryGetValue(id, out res)) { + return res; + } + return null; + } + + public ToolWindowPane FindReplWindow(Guid replId) { + foreach (var idAndWindow in _windows) { + var window = idAndWindow.Value; + if (window.Guid == replId) { + return window; + } + } + return null; + } + + public ToolWindowPane CreateReplWindow(IReplEvaluator/*!*/ evaluator, IContentType/*!*/ contentType, int id, string/*!*/ title, Guid languageServiceGuid, Guid replGuid) { + var service = (IVsUIShell)ServiceProvider.GlobalProvider.GetService(typeof(SVsUIShell)); + var model = (IComponentModel)ServiceProvider.GlobalProvider.GetService(typeof(SComponentModel)); + + var replWindow = new VsReplWindow(model, evaluator, contentType, title, languageServiceGuid, replGuid, id); + + Guid clsId = replWindow.ToolClsid; + Guid toolType = typeof(VsReplWindow).GUID; + Guid empty = Guid.Empty; + IVsWindowFrame frame; + + // we don't pass __VSCREATETOOLWIN.CTW_fMultiInstance because multi instance panes are + // destroyed when closed. We are really multi instance but we don't want to be closed. This + // seems to work fine. + ErrorHandler.ThrowOnFailure( + service.CreateToolWindow( + (uint)(__VSCREATETOOLWIN.CTW_fInitNew | __VSCREATETOOLWIN.CTW_fForceCreate), + (uint)id, + replWindow.GetIVsWindowPane(), + ref clsId, + ref toolType, + ref empty, + null, + title, + null, + out frame + ) + ); + + replWindow.Frame = frame; + + replWindow.OnToolBarAdded(); + _windows[id] = replWindow; + + return replWindow; + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Repl/VsReplWindow.cs b/Tools/IronStudio/IronStudio/IronStudio/Repl/VsReplWindow.cs new file mode 100644 index 0000000000..77909ad5df --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Repl/VsReplWindow.cs @@ -0,0 +1,728 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Design; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Threading; + +using Microsoft.IronStudio.Library.Repl; + +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Editor; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Operations; +using Microsoft.VisualStudio.TextManager.Interop; +using Microsoft.VisualStudio.Utilities; +using Microsoft.Scripting.Utils; + +namespace Microsoft.IronStudio.Repl { + using OleMenuCommandService = Microsoft.VisualStudio.Shell.OleMenuCommandService; + using VSConstants = Microsoft.VisualStudio.VSConstants; + using Microsoft.IronStudio.Core.Repl; + + /// + /// This class implements the tool window exposed by this package and hosts a user control. + /// + /// In Visual Studio tool windows are composed of a frame (implemented by the shell) and a pane, + /// usually implemented by the package implementer. + /// + /// This class derives from the ToolWindowPane class provided from the MPF in order to use its + /// implementation of the IVsUIElementPane interface. + /// + [Guid(VsReplWindow.TypeGuid)] + public class VsReplWindow : ToolWindowPane, IOleCommandTarget, IMixedBuffer, IReplWindow, IVsFindTarget { + public const string TypeGuid = "5adb6033-611f-4d39-a193-57a717115c0f"; + + // This is the user control hosted by the tool window; it is exposed to the base class + // using the Content property. Note that, even if this class implements IDispose, we are + // not calling Dispose on this object. This is because ToolWindowPane calls Dispose on + // the object returned by the Content property. + private IVsEditorAdaptersFactoryService _adapterFactory; + private Dictionary _commands2k = new Dictionary(); + private Dictionary _commands97 = new Dictionary(); + private IVsFindTarget _findTarget; + private IVsTextView _view; + private OleMenuCommandService _commandService; + private readonly Guid _guid, _langSvcGuid; + + private ReplWindow _replWindow; // non-null until disposed + private object _content; + + private static Guid DefaultFileType = new Guid(0x8239bec4, 0xee87, 0x11d0, 0x8c, 0x98, 0x0, 0xc0, 0x4f, 0xc2, 0xab, 0x22); + + /// + /// Standard constructor for the tool window. + /// + public VsReplWindow(IComponentModel/*!*/ model, IReplEvaluator/*!*/ evaluator, IContentType/*!*/ contentType, string/*!*/ title, Guid languageServiceGuid, Guid replGuid, int? id) : + base(null) { + + if (id != null) { + IronStudioPackage.Instance.SaveReplInfo(id.Value, evaluator, contentType, title, languageServiceGuid, replGuid); + } + _replWindow = new VsReplWindowImpl(model, this, evaluator, contentType, title); + + _guid = replGuid; + _langSvcGuid = languageServiceGuid; + + // Set the window title reading it from the resources.z + Caption = _replWindow.Title; + + // Set the image that will appear on the tab of the window frame + // when docked with an other window + // The resource ID correspond to the one defined in the resx file + // while the Index is the offset in the bitmap strip. Each image in + // the strip being 16x16. + BitmapResourceID = 301; + BitmapIndex = 1; + } + + #region IReplWindow + + /// + /// Gets the ITextCaret in which the REPL window is executing. + /// + public ITextCaret Caret { + get { + return _replWindow.Caret; + } + } + + /// + /// WPF Content of the Repl Window + /// + FrameworkElement IReplWindow.Content { + get { + return _replWindow.Content as FrameworkElement; + } + } + + /// + /// Content type in the Repl Window + /// + public IContentType ContentType { + get { + return _replWindow.ContentType; + } + } + + /// + /// Gets the IWpfTextView in which the REPL window is executing. + /// + public IWpfTextView CurrentView { + get { + return _replWindow.CurrentView; + } + } + + /// + /// The language evaluator used in Repl Window + /// + public IReplEvaluator Evaluator { + get { + return _replWindow.Evaluator; + } + } + + public bool UseSmartUpDown { + get { + return _replWindow.UseSmartUpDown; + } + set { + _replWindow.UseSmartUpDown = value; + } + } + + /// + /// Gets or sets whether output from scripts should be echoed to the window. + /// + public bool ShowOutput { + get { + return _replWindow.ShowOutput; + } + set { + _replWindow.ShowOutput = value; + } + } + + /// + /// Title of the Repl Window + /// + public string Title { + get { + return _replWindow.Title; + } + } + + /// + /// See IReplWindow + /// + public void ClearScreen() { + _replWindow.ClearScreen(); + } + + /// + /// See IReplWindow + /// + public void Focus() { + _replWindow.Focus(); + } + + /// + /// See IReplWindow + /// + public void PasteText(string text) { + _replWindow.PasteText(text); + } + + /// + /// See IReplWindow + /// + public void Reset() { + _replWindow.Reset(); + } + + public void AbortCommand() { + _replWindow.AbortCommand(); + } + + /// + /// See IReplWindow + /// + public void WriteLine(string text) { + _replWindow.WriteLine(text); + } + + #endregion + + #region IMixedBuffer + + /// + /// See IMixedBuffer + /// + public SnapshotSpan[] GetLanguageSpans(ITextSnapshot snapshot) { + return _replWindow.GetLanguageSpans(snapshot); + } + + /// + /// See IMixedBuffer + /// + public SnapshotSpan? GetLanguageSpanForLine(ITextSnapshot snapshot, int line) { + return _replWindow.GetLanguageSpanForLine(snapshot, line); + } + + #endregion + + #region Public Members + + public Guid Guid { get { return _guid; } } + + public Guid LanguageServiceGuid { + get { + return _langSvcGuid; + } + } + + #endregion + + /// + /// A command filter which runs before the text view for all commands used for certain commands we need to intercept. + /// + class EarlyCommandFilter : IOleCommandTarget { + internal IOleCommandTarget _nextTarget; + private VsReplWindow _vsReplWindow; + + public EarlyCommandFilter(VsReplWindow vsReplWindow) { + _vsReplWindow = vsReplWindow; + } + + #region IOleCommandTarget Members + + public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { + if (pguidCmdGroup == VSConstants.VSStd2K) { + switch ((VSConstants.VSStd2KCmdID)nCmdID) { + case VSConstants.VSStd2KCmdID.SHOWCONTEXTMENU: + _vsReplWindow.ShowContextMenu(); + return VSConstants.S_OK; + case VSConstants.VSStd2KCmdID.TYPECHAR: + char typedChar = (char)(ushort)Marshal.GetObjectForNativeVariant(pvaIn); + if (!_vsReplWindow._replWindow.CaretInInputRegion) { + _vsReplWindow.EditorOperations.MoveToEndOfDocument(false); + } + _vsReplWindow.EditorOperations.InsertText(typedChar.ToString()); + return VSConstants.S_OK; + case VSConstants.VSStd2KCmdID.PASTE: + break; + } + } else if (pguidCmdGroup == VSConstants.GUID_VSStandardCommandSet97) { + switch ((VSConstants.VSStd97CmdID)nCmdID) { + case VSConstants.VSStd97CmdID.Paste: + // move the cursor into a valid input region and then paste. + if (!_vsReplWindow._replWindow.CaretInInputRegion) { + _vsReplWindow.EditorOperations.MoveToEndOfDocument(false); + } + + _vsReplWindow.EditorOperations.Paste(); + return VSConstants.S_OK; + } + } + + return _nextTarget.Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); + } + + public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { + return _nextTarget.QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); + } + + #endregion + } + + #region IOleCommandTarget Members + + public int Exec(ref Guid pguidCmdGroup, uint nCmdID, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) { + if (pguidCmdGroup == GuidList.guidIronStudioCmdSet) { + switch (nCmdID) { + case PkgCmdIDList.cmdidBreakRepl: BreakRepl(this, EventArgs.Empty); return VSConstants.S_OK; + case PkgCmdIDList.cmdidSmartExecute: SmartExecute(this, EventArgs.Empty); return VSConstants.S_OK; + case PkgCmdIDList.cmdidResetRepl: ResetRepl(this, EventArgs.Empty); return VSConstants.S_OK; + case PkgCmdIDList.cmdidReplHistoryNext: HistoryNext(this, EventArgs.Empty); return VSConstants.S_OK; + case PkgCmdIDList.cmdidReplHistoryPrevious: HistoryPrevious(this, EventArgs.Empty); return VSConstants.S_OK; + case PkgCmdIDList.cmdidReplClearScreen: _replWindow.ClearScreen(); return VSConstants.S_OK; + case PkgCmdIDList.cmdidBreakLine: _replWindow.BreakLine(); return VSConstants.S_OK; + } + } else if (pguidCmdGroup == VSConstants.VSStd2K) { + switch ((VSConstants.VSStd2KCmdID)nCmdID) { + case VSConstants.VSStd2KCmdID.RETURN: + int res = VSConstants.S_OK; + var position = _replWindow.CurrentView.Caret.Position.BufferPosition.GetContainingLine(); + if (_commandService != null) { + res = ((IOleCommandTarget)_commandService).Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); + } + if (position.LineNumber < _replWindow.CurrentView.Caret.Position.BufferPosition.GetContainingLine().LineNumber) { + _replWindow.TryExecuteInput(); + _replWindow.Caret.EnsureVisible(); + } + return res; + default: + Action action; + if (_commands2k.TryGetValue((VSConstants.VSStd2KCmdID)nCmdID, out action)) { + action(); + return VSConstants.S_OK; + } + break; + } + } else if (pguidCmdGroup == VSConstants.GUID_VSStandardCommandSet97) { + Action action; + if (_commands97.TryGetValue((VSConstants.VSStd97CmdID)nCmdID, out action)) { + action(); + return VSConstants.S_OK; + } + } + if (_commandService != null) { + return ((IOleCommandTarget)_commandService).Exec(ref pguidCmdGroup, nCmdID, nCmdexecopt, pvaIn, pvaOut); + } + + return VSConstants.S_OK; + } + + public int QueryStatus(ref Guid pguidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) { + if (pguidCmdGroup == GuidList.guidIronStudioCmdSet) { + switch (prgCmds[0].cmdID) { + case PkgCmdIDList.cmdidReplHistoryNext: + case PkgCmdIDList.cmdidReplHistoryPrevious: + case PkgCmdIDList.cmdidSmartExecute: + case PkgCmdIDList.cmdidBreakLine: + prgCmds[0].cmdf = (uint)(OLECMDF.OLECMDF_ENABLED | OLECMDF.OLECMDF_SUPPORTED | OLECMDF.OLECMDF_DEFHIDEONCTXTMENU); + return VSConstants.S_OK; + } + } + + int hr = (int)Microsoft.VisualStudio.OLE.Interop.Constants.OLECMDERR_E_NOTSUPPORTED; + + if (_commandService != null) { + hr = ((IOleCommandTarget)_commandService).QueryStatus(ref pguidCmdGroup, cCmds, prgCmds, pCmdText); + } + + return hr; + } + + #endregion + + #region VS Initialization + + /// + /// Subclass so we can create the editor using the old APIs and get the new APIs from that. + /// + private sealed class VsReplWindowImpl : ReplWindow { + private readonly VsReplWindow/*!*/ _window; + + public VsReplWindowImpl(IComponentModel/*!*/ model, VsReplWindow/*!*/ window, IReplEvaluator/*!*/ evaluator, IContentType/*!*/ contentType, string/*!*/ title) + : base(model, evaluator, contentType, title) { + Assert.NotNull(window); + _window = window; + } + + protected override IWpfTextViewHost/*!*/ CreateTextViewHost() { + var adapterFactory = ComponentModel.GetService(); + var provider = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)_window.GetService(typeof(Microsoft.VisualStudio.OLE.Interop.IServiceProvider)); + + // create the buffer adapter and initialize it + IVsTextBuffer bufferAdapter = adapterFactory.CreateVsTextBufferAdapter(provider, ContentType); + int result = bufferAdapter.InitializeContent("", 0); + bufferAdapter.SetLanguageServiceID(_window.LanguageServiceGuid); + + // ITextEditor is available only after InitializeContent was called: + ITextBuffer textBuffer = adapterFactory.GetDataBuffer(bufferAdapter); + + // we need to set IReplProptProvider property before TextViewHost is instantiated so that ReplPromptTaggerProvider can bind to it + if (Evaluator.DisplayPromptInMargin) { + textBuffer.Properties.AddProperty(typeof(IReplPromptProvider), this); + } + + // Create and inititalize text view adapter. + // WARNING: This might trigger various services like IntelliSense, margins, taggers, etc. + IVsTextView textViewAdapter = adapterFactory.CreateVsTextViewAdapter(provider, GetReplRoles()); + ((IVsWindowPane)textViewAdapter).SetSite(provider); + + // make us a code window so we'll have the same colors as a normal code window. + IVsTextEditorPropertyContainer propContainer; + ((IVsTextEditorPropertyCategoryContainer)textViewAdapter).GetPropertyCategory(Microsoft.VisualStudio.Editor.DefGuidList.guidEditPropCategoryViewMasterSettings, out propContainer); + propContainer.SetProperty(VSEDITPROPID.VSEDITPROPID_ViewComposite_AllCodeWindowDefaults, true); + + textViewAdapter.Initialize( + (IVsTextLines)bufferAdapter, + IntPtr.Zero, + (uint)TextViewInitFlags.VIF_HSCROLL | (uint)TextViewInitFlags.VIF_VSCROLL, + new[] { new INITVIEW { fSelectionMargin = 0, fWidgetMargin = 0, fVirtualSpace = 0, fDragDropMove = 1 } } + ); + + // disable change tracking because everything will be changed + var res = adapterFactory.GetWpfTextViewHost(textViewAdapter); + res.TextView.Options.SetOptionValue(DefaultTextViewHostOptions.ChangeTrackingId, false); + return res; + } + } + + protected override void OnCreate() { + _replWindow.Initialize(); + + _adapterFactory = _replWindow.ComponentModel.GetService(); + + var textView = _replWindow.CurrentView; + + SetDefaultFontSize(_replWindow.ComponentModel, textView); + + // create adapters so VS is happy... + var serviceProvider = (Microsoft.VisualStudio.OLE.Interop.IServiceProvider)GetService(typeof(Microsoft.VisualStudio.OLE.Interop.IServiceProvider)); + + _view = _adapterFactory.GetViewAdapter(textView); + _findTarget = _view as IVsFindTarget; + + CurrentView.Closed += OnClose; + + _content = _replWindow.Content; + } + + /// + /// Sets the default font size to match that of a normal editor buffer. + /// + private void SetDefaultFontSize(IComponentModel model, IWpfTextView textView) { + var formatMapSvc = model.GetService(); + var fontsAndColorsSvc = model.GetService(); + var fontCat = new VisualStudio.Editor.FontsAndColorsCategory( + _langSvcGuid, + Microsoft.VisualStudio.Editor.DefGuidList.guidTextEditorFontCategory, + Microsoft.VisualStudio.Editor.DefGuidList.guidTextEditorFontCategory + ); + + var fontInfo = fontsAndColorsSvc.GetFontAndColorInformation(fontCat); + var fontPrefs = fontInfo.GetFontAndColorPreferences(); + var font = System.Drawing.Font.FromHfont(fontPrefs.hRegularViewFont); + + var classMap = formatMapSvc.GetClassificationFormatMap(textView); + var defaultProps = classMap.DefaultTextProperties; + defaultProps = defaultProps.SetFontRenderingEmSize(font.Size); + classMap.DefaultTextProperties = defaultProps; + } + + public override void OnToolWindowCreated() { + Guid commandUiGuid = Microsoft.VisualStudio.VSConstants.GUID_TextEditorFactory; + ((IVsWindowFrame)Frame).SetGuidProperty((int)__VSFPROPID.VSFPROPID_InheritKeyBindings, ref commandUiGuid); + + var earlyFilter = new EarlyCommandFilter(this); + ErrorHandler.ThrowOnFailure(_view.AddCommandFilter(earlyFilter, out earlyFilter._nextTarget)); + _commandService = new OleMenuCommandService(this, (IOleCommandTarget)_view); + + AddCommand(VSConstants.VSStd2KCmdID.CANCEL, () => _replWindow.Cancel()); + + AddCommand(VSConstants.VSStd2KCmdID.UP, _replWindow.SmartUpArrow); + AddCommand(VSConstants.VSStd2KCmdID.DOWN, _replWindow.SmartDownArrow); + AddCommand(VSConstants.VSStd2KCmdID.BOL, () => _replWindow.Home(false)); + AddCommand(VSConstants.VSStd2KCmdID.BOL_EXT, () => _replWindow.Home(true)); + + AddCommand(VSConstants.VSStd2KCmdID.SHOWCONTEXTMENU, ShowContextMenu); + + var id = new CommandID(GuidList.guidIronStudioCmdSet, (int)PkgCmdIDList.cmdidBreakRepl); + var cmd = new OleMenuCommand(BreakRepl, id); + _commandService.AddCommand(cmd); + + id = new CommandID(GuidList.guidIronStudioCmdSet, (int)PkgCmdIDList.cmdidResetRepl); + cmd = new OleMenuCommand(ResetRepl, id); + _commandService.AddCommand(cmd); + + id = new CommandID(GuidList.guidIronStudioCmdSet, (int)PkgCmdIDList.cmdidSmartExecute); + cmd = new OleMenuCommand(SmartExecute, id); + _commandService.AddCommand(cmd); + + + id = new CommandID(GuidList.guidIronStudioCmdSet, (int)PkgCmdIDList.cmdidReplHistoryNext); + cmd = new OleMenuCommand(SmartExecute, id); + _commandService.AddCommand(cmd); + + id = new CommandID(GuidList.guidIronStudioCmdSet, (int)PkgCmdIDList.cmdidBreakLine); + cmd = new OleMenuCommand(SmartExecute, id); + _commandService.AddCommand(cmd); + + + id = new CommandID(GuidList.guidIronStudioCmdSet, (int)PkgCmdIDList.cmdidReplHistoryPrevious); + cmd = new OleMenuCommand(SmartExecute, id); + _commandService.AddCommand(cmd); + + base.OnToolWindowCreated(); + } + + public void HistoryNext(object sender, EventArgs e) { + _replWindow.HistoryNext(); + } + + public void HistoryPrevious(object sender, EventArgs e) { + _replWindow.HistoryPrevious(); + } + + public void ShowContextMenu() { + var uishell = (IVsUIShell)GetService(typeof(SVsUIShell)); + if (uishell != null) { + var pt = System.Windows.Forms.Cursor.Position; + var pnts = new[] { new POINTS { x = (short)pt.X, y = (short)pt.Y } }; + var guid = GuidList.guidIronStudioCmdSet; + int hr = uishell.ShowContextMenu( + 0, + ref guid, + 0x2100, + pnts, + _replWindow.CurrentView as IOleCommandTarget); + + ErrorHandler.ThrowOnFailure(hr); + } + } + + public void BreakRepl(object sender, EventArgs args) { + _replWindow.AbortCommand(); + } + + public void ResetRepl(object sender, EventArgs args) { + _replWindow.Reset(); + } + + public void SmartExecute(object sender, EventArgs args) { + _replWindow.ExecuteOrPasteSelected(); + } + + private void AddCommand(VSConstants.VSStd2KCmdID command, Action action) { + var id = new CommandID(typeof(VSConstants.VSStd2KCmdID).GUID, (int)command); + var cmd = new OleMenuCommand(OnReturn, id); + _commandService.AddCommand(cmd); + _commands2k[command] = action; + } + + private void AddCommand(VSConstants.VSStd97CmdID command, Action action) { + var id = new CommandID(typeof(VSConstants.VSStd97CmdID).GUID, (int)command); + var cmd = new OleMenuCommand(OnReturn, id); + _commandService.AddCommand(cmd); + _commands97[command] = action; + } + + private void OnReturn(object sender, EventArgs args) { + } + + #endregion + + #region Overridden Methods + + /// + /// This property returns the control that should be hosted in the Tool Window. + /// It can be either a FrameworkElement (for easy creation of toolwindows hosting WPF content), + /// or it can be an object implementing one of the IVsUIWPFElement or IVsUIWin32Element interfaces. + /// + public override object Content { + get { + Debug.Assert(_content != null); + return _content; + } + set { + ContractUtils.RequiresNotNull(value, "value"); + _content = value; + } + } + + /// + /// Return the service of the given type. + /// This override is needed to be able to use a different command service from the one + /// implemented in the base class. + /// + protected override object GetService(Type serviceType) { + if ((typeof(IOleCommandTarget) == serviceType) || + (typeof(IMenuCommandService) == serviceType)) { + if (null != _commandService) { + return _commandService; + } + } + return base.GetService(serviceType); + } + + #endregion + + #region Private Methods + + private IEditorOperations EditorOperations { + get { + return _replWindow.EditorOperations; + } + } + + private void OnClose(object sender, EventArgs ea) { + _replWindow.Dispose(); + _replWindow = null; + + try { + //File.Delete(FilePath); + } catch (Exception e) { + // TODO: Log error + Debug.WriteLine("Error while deleting temporary REPL file: {0}", e.ToString()); + } + } + + #endregion + + public int Find(string pszSearch, uint grfOptions, int fResetStartPoint, IVsFindHelper pHelper, out uint pResult) { + if (_findTarget != null) { + return _findTarget.Find(pszSearch, grfOptions, fResetStartPoint, pHelper, out pResult); + } + pResult = 0; + return VSConstants.E_NOTIMPL; + } + + public int GetCapabilities(bool[] pfImage, uint[] pgrfOptions) { + if (_findTarget != null) { + return _findTarget.GetCapabilities(pfImage, pgrfOptions); + } + return VSConstants.E_NOTIMPL; + } + + public int GetCurrentSpan(TextSpan[] pts) { + if (_findTarget != null) { + return _findTarget.GetCurrentSpan(pts); + } + return VSConstants.E_NOTIMPL; + } + + public int GetFindState(out object ppunk) { + if (_findTarget != null) { + return _findTarget.GetFindState(out ppunk); + } + ppunk = null; + return VSConstants.E_NOTIMPL; + + } + + public int GetMatchRect(RECT[] prc) { + if (_findTarget != null) { + return _findTarget.GetMatchRect(prc); + } + return VSConstants.E_NOTIMPL; + } + + public int GetProperty(uint propid, out object pvar) { + if (_findTarget != null) { + return _findTarget.GetProperty(propid, out pvar); + } + pvar = null; + return VSConstants.E_NOTIMPL; + } + + public int GetSearchImage(uint grfOptions, IVsTextSpanSet[] ppSpans, out IVsTextImage ppTextImage) { + if (_findTarget != null) { + return _findTarget.GetSearchImage(grfOptions, ppSpans, out ppTextImage); + } + ppTextImage = null; + return VSConstants.E_NOTIMPL; + } + + public int MarkSpan(TextSpan[] pts) { + if (_findTarget != null) { + return _findTarget.MarkSpan(pts); + } + return VSConstants.E_NOTIMPL; + } + + public int NavigateTo(TextSpan[] pts) { + if (_findTarget != null) { + return _findTarget.NavigateTo(pts); + } + return VSConstants.E_NOTIMPL; + } + + public int NotifyFindTarget(uint notification) { + if (_findTarget != null) { + return _findTarget.NotifyFindTarget(notification); + } + return VSConstants.E_NOTIMPL; + } + + public int Replace(string pszSearch, string pszReplace, uint grfOptions, int fResetStartPoint, IVsFindHelper pHelper, out int pfReplaced) { + if (_findTarget != null) { + return _findTarget.Replace(pszSearch, pszReplace, grfOptions, fResetStartPoint, pHelper, out pfReplaced); + } + pfReplaced = 0; + return VSConstants.E_NOTIMPL; + } + + public int SetFindState(object pUnk) { + if (_findTarget != null) { + return _findTarget.SetFindState(pUnk); + } + return VSConstants.E_NOTIMPL; + } + + internal void Cancel() { + _replWindow.Cancel(); + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Utils/ProgressForm.cs b/Tools/IronStudio/IronStudio/IronStudio/Utils/ProgressForm.cs new file mode 100644 index 0000000000..2254b49c5e --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Utils/ProgressForm.cs @@ -0,0 +1,54 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Data; +using System.Drawing; +using System.Linq; +using System.Text; +using System.Windows.Forms; +using System.Threading; + +namespace Microsoft.IronStudio.Utils { + public partial class ProgressForm : Form { + public EventWaitHandle CancellationEvent { get; set; } + public bool Cancelled { get; private set; } + + public ProgressForm(int maxProgress = 100) { + InitializeComponent(); + _progressBar.Maximum = maxProgress; + } + + private void CancelButton_Click(object sender, EventArgs e) { + _cancelButton.Enabled = false; + Cancelled = true; + BackgroundWorker.CancelAsync(); + var handle = CancellationEvent; + if (handle != null) { + handle.Set(); + } + } + + private void BackgroundWorker_RunWorkerCompleted(object sender, RunWorkerCompletedEventArgs e) { + _cancelButton.Enabled = false; + Close(); + } + + private void BackgroundWorker_ProgressChanged(object sender, ProgressChangedEventArgs e) { + _progressBar.Value = e.ProgressPercentage; + } + } +} diff --git a/Tools/IronStudio/IronStudio/IronStudio/Utils/ProgressForm.designer.cs b/Tools/IronStudio/IronStudio/IronStudio/Utils/ProgressForm.designer.cs new file mode 100644 index 0000000000..0c3381b9fb --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Utils/ProgressForm.designer.cs @@ -0,0 +1,75 @@ +namespace Microsoft.IronStudio.Utils { + partial class ProgressForm { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) { + if (disposing && (components != null)) { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() { + this.BackgroundWorker = new System.ComponentModel.BackgroundWorker(); + this._progressBar = new System.Windows.Forms.ProgressBar(); + this._cancelButton = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // BackgroundWorker + // + this.BackgroundWorker.WorkerReportsProgress = true; + this.BackgroundWorker.WorkerSupportsCancellation = true; + this.BackgroundWorker.ProgressChanged += new System.ComponentModel.ProgressChangedEventHandler(this.BackgroundWorker_ProgressChanged); + this.BackgroundWorker.RunWorkerCompleted += new System.ComponentModel.RunWorkerCompletedEventHandler(this.BackgroundWorker_RunWorkerCompleted); + // + // Progress + // + this._progressBar.ForeColor = System.Drawing.Color.FromArgb(((int)(((byte)(0)))), ((int)(((byte)(192)))), ((int)(((byte)(0))))); + this._progressBar.Location = new System.Drawing.Point(30, 25); + this._progressBar.Name = "Progress"; + this._progressBar.Size = new System.Drawing.Size(225, 23); + this._progressBar.TabIndex = 0; + // + // CancelButton + // + this._cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + this._cancelButton.Location = new System.Drawing.Point(106, 62); + this._cancelButton.Name = "CancelButton"; + this._cancelButton.Size = new System.Drawing.Size(75, 23); + this._cancelButton.TabIndex = 1; + this._cancelButton.Text = "Cancel"; + this._cancelButton.UseVisualStyleBackColor = true; + this._cancelButton.Click += new System.EventHandler(this.CancelButton_Click); + // + // ProgressForm + // + this.AutoScaleDimensions = new System.Drawing.SizeF(6F, 13F); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.ClientSize = new System.Drawing.Size(284, 104); + this.Controls.Add(this._cancelButton); + this.Controls.Add(this._progressBar); + this.Name = "ProgressForm"; + this.ResumeLayout(false); + + } + + #endregion + + private System.Windows.Forms.ProgressBar _progressBar; + private System.Windows.Forms.Button _cancelButton; + public System.ComponentModel.BackgroundWorker BackgroundWorker; + } +} \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/IronStudio/Utils/ProgressForm.resx b/Tools/IronStudio/IronStudio/IronStudio/Utils/ProgressForm.resx new file mode 100644 index 0000000000..6c42bab797 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/Utils/ProgressForm.resx @@ -0,0 +1,123 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + 17, 17 + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/IronStudio/VsExtensions.cs b/Tools/IronStudio/IronStudio/IronStudio/VsExtensions.cs new file mode 100644 index 0000000000..153e2ff149 --- /dev/null +++ b/Tools/IronStudio/IronStudio/IronStudio/VsExtensions.cs @@ -0,0 +1,73 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.IO; + +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Project.Automation; +using Microsoft.IronStudio.Project; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio; + +namespace Microsoft.IronStudio { + static class VsExtensions { + public static string GetFilePath(this ITextView textView) { + ITextDocument textDocument; + if (textView.TextBuffer.Properties.TryGetProperty(typeof(ITextDocument), out textDocument)) { + return textDocument.FilePath; + } else { + return null; + } + } + + internal static ITrackingSpan CreateTrackingSpan(this IIntellisenseSession session, ITextBuffer buffer) { + var triggerPoint = session.GetTriggerPoint(buffer); + var position = session.GetTriggerPoint(buffer).GetPosition(session.TextView.TextSnapshot); + + var snapshot = buffer.CurrentSnapshot; + if (position == snapshot.Length) { + return snapshot.CreateTrackingSpan(position, 0, SpanTrackingMode.EdgeInclusive); + } else { + return snapshot.CreateTrackingSpan(position, 1, SpanTrackingMode.EdgeInclusive); + } + } + + public static CommonProjectNode GetCommonProject(this EnvDTE.Project project) { + OAProject oaProj = project as OAProject; + if (oaProj != null) { + var common = oaProj.Project as CommonProjectNode; + if (common != null) { + return common; + } + } + return null; + } + + public static EnvDTE.Project GetProject(this IVsHierarchy hierarchy) { + object project; + + ErrorHandler.ThrowOnFailure( + hierarchy.GetProperty( + VSConstants.VSITEMID_ROOT, + (int)__VSHPROPID.VSHPROPID_ExtObject, + out project + ) + ); + + return project as EnvDTE.Project; + } + } +} diff --git a/Tools/IronStudio/IronStudio/PkgCmdID.cs b/Tools/IronStudio/IronStudio/PkgCmdID.cs new file mode 100644 index 0000000000..adc022270a --- /dev/null +++ b/Tools/IronStudio/IronStudio/PkgCmdID.cs @@ -0,0 +1,33 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +// PkgCmdID.cs +// MUST match PkgCmdID.h +using System; + +namespace Microsoft.IronStudio +{ + static class PkgCmdIDList + { + + public const uint cmdidSmartExecute = 0x103; + public const uint cmdidBreakRepl = 0x104; + public const uint cmdidResetRepl = 0x105; + public const uint cmdidReplHistoryNext = 0x0106; + public const uint cmdidReplHistoryPrevious = 0x0107; + public const uint cmdidReplClearScreen = 0x0108; + public const uint cmdidBreakLine = 0x0109; + + }; +} \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/Properties/AssemblyInfo.cs b/Tools/IronStudio/IronStudio/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..4ed3260ced --- /dev/null +++ b/Tools/IronStudio/IronStudio/Properties/AssemblyInfo.cs @@ -0,0 +1,43 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("IronStudio")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("IronStudio")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7942ec78-22df-4632-bc99-4877f1fb26c9")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] +#if MS_SIGNED +[assembly: InternalsVisibleTo("IronPythonTools, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +[assembly: InternalsVisibleTo("IronRubyTools, PublicKey=0024000004800000940000000602000000240000525341310004000001000100b5fc90e7027f67871e773a8fde8938c81dd402ba65b9201d60593e96c492651e889cc13f1415ebb53fac1131ae0bd333c5ee6021672d9718ea31a8aebd0da0072f25d87dba6fc90ffd598ed4da35e44c398c454307e8e33b8426143daec9f596836f97c8f74750e5975c64e2189f45def46b2a2b1247adc3652bf5c308055da9")] +#else +[assembly: InternalsVisibleTo("IronPythonTools, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c10ce00dd2e0ce5046d68183d3ad035b47e92bf0ce7bcf8a03a217ca5d0b0c7db973fdf97579b52b502a23d4069dbf043389e1ab65a1d6c508a9837f3e2350f15e05cc63c0fc4b0410867a51919090e4c33f80203e9b0035b21c32bae20f98b068f90d99a50133a5336480d94039b176519f5fd8524765f33be43da65c4b68ba")] +[assembly: InternalsVisibleTo("IronRubyTools, PublicKey=0024000004800000940000000602000000240000525341310004000001000100c10ce00dd2e0ce5046d68183d3ad035b47e92bf0ce7bcf8a03a217ca5d0b0c7db973fdf97579b52b502a23d4069dbf043389e1ab65a1d6c508a9837f3e2350f15e05cc63c0fc4b0410867a51919090e4c33f80203e9b0035b21c32bae20f98b068f90d99a50133a5336480d94039b176519f5fd8524765f33be43da65c4b68ba")] +#endif diff --git a/Tools/IronStudio/IronStudio/Resources.Designer.cs b/Tools/IronStudio/IronStudio/Resources.Designer.cs new file mode 100644 index 0000000000..a817dd4b63 --- /dev/null +++ b/Tools/IronStudio/IronStudio/Resources.Designer.cs @@ -0,0 +1,72 @@ +//------------------------------------------------------------------------------ +// +// This code was generated by a tool. +// Runtime Version:4.0.30128.1 +// +// Changes to this file may cause incorrect behavior and will be lost if +// the code is regenerated. +// +//------------------------------------------------------------------------------ + +namespace Microsoft.IronStudio { + using System; + + + /// + /// A strongly-typed resource class, for looking up localized strings, etc. + /// + // This class was auto-generated by the StronglyTypedResourceBuilder + // class via a tool like ResGen or Visual Studio. + // To add or remove a member, edit your .ResX file then rerun ResGen + // with the /str option, or rebuild your VS project. + [global::System.CodeDom.Compiler.GeneratedCodeAttribute("System.Resources.Tools.StronglyTypedResourceBuilder", "4.0.0.0")] + [global::System.Diagnostics.DebuggerNonUserCodeAttribute()] + [global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()] + internal class Resources { + + private static global::System.Resources.ResourceManager resourceMan; + + private static global::System.Globalization.CultureInfo resourceCulture; + + [global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Resources() { + } + + /// + /// Returns the cached ResourceManager instance used by this class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Resources.ResourceManager ResourceManager { + get { + if (object.ReferenceEquals(resourceMan, null)) { + global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("Microsoft.Resources", typeof(Resources).Assembly); + resourceMan = temp; + } + return resourceMan; + } + } + + /// + /// Overrides the current thread's CurrentUICulture property for all + /// resource lookups using this strongly typed resource class. + /// + [global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)] + internal static global::System.Globalization.CultureInfo Culture { + get { + return resourceCulture; + } + set { + resourceCulture = value; + } + } + + /// + /// Looks up a localized string similar to Can not create tool window.. + /// + internal static string CanNotCreateWindow { + get { + return ResourceManager.GetString("CanNotCreateWindow", resourceCulture); + } + } + } +} diff --git a/Tools/IronStudio/IronStudio/Resources.resx b/Tools/IronStudio/IronStudio/Resources.resx new file mode 100644 index 0000000000..1aea5e9d15 --- /dev/null +++ b/Tools/IronStudio/IronStudio/Resources.resx @@ -0,0 +1,129 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Can not create tool window. + + + References + + + IronPython Command Line + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/Resources/CancelEvaluation.bmp b/Tools/IronStudio/IronStudio/Resources/CancelEvaluation.bmp new file mode 100644 index 0000000000..04e0a49466 Binary files /dev/null and b/Tools/IronStudio/IronStudio/Resources/CancelEvaluation.bmp differ diff --git a/Tools/IronStudio/IronStudio/Resources/Images_32bit.png b/Tools/IronStudio/IronStudio/Resources/Images_32bit.png new file mode 100644 index 0000000000..6097d8f78e Binary files /dev/null and b/Tools/IronStudio/IronStudio/Resources/Images_32bit.png differ diff --git a/Tools/IronStudio/IronStudio/Resources/Package.ico b/Tools/IronStudio/IronStudio/Resources/Package.ico new file mode 100644 index 0000000000..ea3b23fe8d Binary files /dev/null and b/Tools/IronStudio/IronStudio/Resources/Package.ico differ diff --git a/Tools/IronStudio/IronStudio/Resources/ResetSession.bmp b/Tools/IronStudio/IronStudio/Resources/ResetSession.bmp new file mode 100644 index 0000000000..5544218212 Binary files /dev/null and b/Tools/IronStudio/IronStudio/Resources/ResetSession.bmp differ diff --git a/Tools/IronStudio/IronStudio/Resources/completionset.bmp b/Tools/IronStudio/IronStudio/Resources/completionset.bmp new file mode 100644 index 0000000000..d4ea8c21a2 Binary files /dev/null and b/Tools/IronStudio/IronStudio/Resources/completionset.bmp differ diff --git a/Tools/IronStudio/IronStudio/VSPackage.resx b/Tools/IronStudio/IronStudio/VSPackage.resx new file mode 100644 index 0000000000..80379a6f51 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VSPackage.resx @@ -0,0 +1,130 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Resources\Package.ico;System.Drawing.Icon, System.Drawing, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a + + + Text Editor! + + + Interactive Console + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio.Project.resx b/Tools/IronStudio/IronStudio/VisualStudio.Project.resx new file mode 100644 index 0000000000..03b6146531 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio.Project.resx @@ -0,0 +1,509 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Failed to add file '{0}' to project as project is null. + + + Advanced + Project Property Page Caption + + + A reference to component '{0}' cannot be added. A reference to the component already exists in the project. + ReferenceAlreadyExists error message + + + Could not load attribute '{0}' from project file '{1}'. + + + Build Action + Project Build Property Name + + + How the file relates to the build and deployment process + Project Build Property Description + + + Verbosity + + + Specify how much information is included in the build output + + + ECMA-335 CLI compatible framework (location must be provided) + Target platform drop down option + + + Compile + Build Action - drop down option + + + If you change a file name extension, the file may become unusable. Are you sure you want to change it? + + + A file with name '{0}' already exists and is open in an editor. Please give a unique name to the item you are adding, or delete the existing item first. + + + Cannot save '{0}' as it is not open in the editor. + + + Content + Build Action - drop down option + + + Copy Local + File property + + + Indicates whether the reference will be copied to the output directory. + + + Detailed + + + Diagnostic + + + Directory already exists + + + Error opening specified view '{0}' using editor '{1}' + + + Embedded Resource + Build Action - drop down option + + + error + + + Files and folders cannot be: +- Empty strings +- System reserved names, including 'CON', 'AUX', PRN', 'COM1' or 'LPT2' +- contain only '.' +- have any of the following characters: / ? : & \ * " < > | # % + + + The name you provided is not a valid project name. + + + MSBuild path not found in registry. Please reinstall to fix the problem. + + + Error Saving File + + + Console Application + + + Expecting object of type {0}. + + + Failed to retrieve MSBuild property {0} from the project file. + The exception message thrown when getting a property from msbuild fails. + + + A file with the same name '{0}' already exists. Do you want to overwrite it? + + + A file with the same name '{0}' already exists. + + + Destination File Exists + + + A file of this name is already part of the project. Do you want to overwrite it? + + + Destination File Exists + + + A file or folder with the name '{0}' already exists on disk at this location. Please choose another name. + + + Error Copying File + + + File Name + + + File and folder names cannot contain a leading period. + + + The name of the file or folder + + + Folder Name + + + Name of this folder + + + Full Path + + + Location of the file + + + The item '{0}' does not exist in the project directory. It may have been moved, renamed or deleted. + + + Cannot save '{0}' outside the project directory. Linked items are not supported. + + + Class Library + + + Minimal + + + Misc + + + None + + + Normal + + + Item is not available or corrupted and thus cannot be pasted. + + + Program + + + Project + + + A reference to library '{0}' cannot be added. Adding this project as a reference would cause a circular dependency. + + + Project File + + + The name of the file containing build, configuration, and other information about the project. + + + Project Folder + + + The absolute location of the project. + + + Quiet + + + (Name) + + + References + + + Display name of the reference + + + Rename directory failed. {0} + + + RTL_False + + + Save? + + + Do you want to save modified documents? + + + The project file can only be saved into the project location '{0}'. + + + Error opening specified view '{0}' using standard editor. + + + URL + + + You are trying to use an item that has already been deleted or that does not exist. + + + Microsoft .Net Framework v1.0 + + + Microsoft .Net Framework v1.1 + + + Microsoft .Net Framework v2.0 + + + warning + + + Windows Application + + + Add Reference + + + Automation object invalid. + + + The command you are attempting cannot be completed because the file '{0}' that must be modified cannot be changed. If the file is under source control, you may want to check it out; if the file is read-only on disk, you may want to change its attributes. + MsgBox + + + Parameter must be a valid guid. + Error message in the ArgumentException for a parameter that is not a valid guid. + + + Invalid logger type\nExpected: {0}\nReceived: {1} + Error message thrown for when an invalid logger type is set. + + + InvalidParameter + Generic error message for invalid parameters. + + + ParameterCannotBeNullOEmpty + Error message for string parameters that cannot be null or empty. + + + Parameter must be a valid item identifier. + Error message thrown when an invalid item id is retrieved. + + + File Properties + + + Folder Properties + + + Project Properties + + + Reference Properties + + + A file or folder with the name '{0}' already exists on disk at this location. Please choose another name. + + + Build + + + Output Path + + + The path to the primary output + + + The complete path to '{0}' exceeds the maximum number of characters permitted by the file system. + + + Custom Tool + + + Specifies the tool that transforms a file at design time and places the output of that transformation into another file. For example, a dataset (.xsd) file comes with a default custom tool. + + + Custom Tool Namespace + + + The namespace into which the output of the custom tool is placed. + + + Primary Output + + + Content Files + + + Documentation Files + + + Localized Resources + + + Source Files + + + Debug Symbols + + + XML Serialization Assemblies + + + Contains the DLL or EXE built by the project. + + + Contains all content files in the project. + + + Contains the XML Documentation files for the project. + + + Contains the satellite assemblies for each culture's resources. + + + Contains all source files in the project. + + + Contains the debugging files for the project. + + + Contains the XML serialization assemblies for the project. + + + The project file '{0}' has been modified outside of Visual Studio. Do you want to reload the project? + + + The nested project has failed to reload. + + + The item '{0}' cannot be found on disk, either because it has been renamed, deleted, or moved to a new location. + + + A reference to {0} could not be added. Please make sure that the file is accessible, and that it is a valid assembly or COM component. + + + An 'Import' of the file '{1}' was found in the project file '{0}'. This file is not registered as a safe file to import, and could contain targets and tasks that are harmful. If this imported file is indeed considered safe, then it can be registered by writing to the registry key {2}. + + + An 'Import' of the file '{1}' was found in the user project file '{0}'. All imports in user project files are considered unsafe. + + + One or more items named '{1}' were found in the project file '{0}'. These items normally should not be specified in the project file. They can change the way targets and tasks are executed during project load, and this could have harmful effects. + + + A property named '{1}' was found in the project file '{0}'. This property normally should not be specified in the project file. Its value can change the way targets and tasks are executed during project load, and this could have harmful effects. + + + A 'UsingTask' tag which registers the '{1}' task was found in the project file '{0}'. 'UsingTask' tags in the project file take precedence over those in the imported .TARGETS files, and therefore could be used to execute arbitrary code during an otherwise unmodified build process. + + + A 'Target' named '{1}' was found in the project file '{0}'. The tasks within this target could contain arbitrary code and may get executed as soon as the project is loaded in the IDE. + + + An item referring to the file '{1}' was found in the project file '{0}'. Since this file is located within a system directory, root directory, or network share, it could be harmful to write to this file. + + + The project location is not trusted:{0}{0}{1}{0}{0}Running the application may result in security exceptions when it {0}attempts to perform actions which require full trust.{0}{0}Click OK to ignore and continue. + + + Exception was thrown during BuildBegin event\n{0} + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/AssemblyReferenceNode.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/AssemblyReferenceNode.cs new file mode 100644 index 0000000000..b9ad6d4b07 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/AssemblyReferenceNode.cs @@ -0,0 +1,485 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Reflection; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using MSBuild = Microsoft.Build.Evaluation; +using MSBuildExecution = Microsoft.Build.Execution; +using Microsoft.Build.Evaluation; +using Microsoft.Build.Framework; + +namespace Microsoft.VisualStudio.Project +{ + [CLSCompliant(false)] + [ComVisible(true)] + public class AssemblyReferenceNode : ReferenceNode + { + #region fieds + /// + /// The name of the assembly this refernce represents + /// + private System.Reflection.AssemblyName assemblyName; + private AssemblyName resolvedAssemblyName; + + private string assemblyPath = String.Empty; + + /// + /// Defines the listener that would listen on file changes on the nested project node. + /// + private FileChangeManager fileChangeListener; + + /// + /// A flag for specifying if the object was disposed. + /// + private bool isDisposed; + #endregion + + #region properties + /// + /// The name of the assembly this reference represents. + /// + /// + internal System.Reflection.AssemblyName AssemblyName + { + get + { + return this.assemblyName; + } + } + + /// + /// Returns the name of the assembly this reference refers to on this specific + /// machine. It can be different from the AssemblyName property because it can + /// be more specific. + /// + internal System.Reflection.AssemblyName ResolvedAssembly + { + get { return resolvedAssemblyName; } + } + + public override string Url + { + get + { + return this.assemblyPath; + } + } + + public override string Caption + { + get + { + return this.assemblyName.Name; + } + } + + private Automation.OAAssemblyReference assemblyRef; + internal override object Object + { + get + { + if(null == assemblyRef) + { + assemblyRef = new Automation.OAAssemblyReference(this); + } + return assemblyRef; + } + } + #endregion + + #region ctors + /// + /// Constructor for the ReferenceNode + /// + public AssemblyReferenceNode(ProjectNode root, ProjectElement element) + : base(root, element) + { + this.GetPathNameFromProjectFile(); + + this.InitializeFileChangeEvents(); + + string include = this.ItemNode.GetMetadata(ProjectFileConstants.Include); + + this.CreateFromAssemblyName(new System.Reflection.AssemblyName(include)); + } + + /// + /// Constructor for the AssemblyReferenceNode + /// + public AssemblyReferenceNode(ProjectNode root, string assemblyPath) + : base(root) + { + // Validate the input parameters. + if(null == root) + { + throw new ArgumentNullException("root"); + } + if(string.IsNullOrEmpty(assemblyPath)) + { + throw new ArgumentNullException("assemblyPath"); + } + + this.InitializeFileChangeEvents(); + + // The assemblyPath variable can be an actual path on disk or a generic assembly name. + if(File.Exists(assemblyPath)) + { + // The assemblyPath parameter is an actual file on disk; try to load it. + this.assemblyName = System.Reflection.AssemblyName.GetAssemblyName(assemblyPath); + this.assemblyPath = assemblyPath; + + // We register with listeningto chnages onteh path here. The rest of teh cases will call into resolving the assembly and registration is done there. + this.fileChangeListener.ObserveItem(this.assemblyPath); + } + else + { + // The file does not exist on disk. This can be because the file / path is not + // correct or because this is not a path, but an assembly name. + // Try to resolve the reference as an assembly name. + this.CreateFromAssemblyName(new System.Reflection.AssemblyName(assemblyPath)); + } + } + #endregion + + #region methods + /// + /// Closes the node. + /// + /// + public override int Close() + { + try + { + this.Dispose(true); + } + finally + { + base.Close(); + } + + return VSConstants.S_OK; + } + + /// + /// Links a reference node to the project and hierarchy. + /// + protected override void BindReferenceData() + { + Debug.Assert(this.assemblyName != null, "The AssemblyName field has not been initialized"); + + // If the item has not been set correctly like in case of a new reference added it now. + // The constructor for the AssemblyReference node will create a default project item. In that case the Item is null. + // We need to specify here the correct project element. + if(this.ItemNode == null || this.ItemNode.Item == null) + { + this.ItemNode = new ProjectElement(this.ProjectMgr, this.assemblyName.FullName, ProjectFileConstants.Reference); + } + + // Set the basic information we know about + this.ItemNode.SetMetadata(ProjectFileConstants.Name, this.assemblyName.Name); + this.ItemNode.SetMetadata(ProjectFileConstants.AssemblyName, Path.GetFileName(this.assemblyPath)); + + this.SetReferenceProperties(); + } + + /// + /// Disposes the node + /// + /// + protected override void Dispose(bool disposing) + { + if(this.isDisposed) + { + return; + } + + try + { + this.UnregisterFromFileChangeService(); + } + finally + { + base.Dispose(disposing); + this.isDisposed = true; + } + } + + private void CreateFromAssemblyName(AssemblyName name) + { + this.assemblyName = name; + + // Use MsBuild to resolve the assemblyname + this.ResolveAssemblyReference(); + + if(String.IsNullOrEmpty(this.assemblyPath) && (null != this.ItemNode.Item)) + { + // Try to get the assmbly name from the hintpath. + this.GetPathNameFromProjectFile(); + if(this.assemblyPath == null) + { + // Try to get the assembly name from the path + this.assemblyName = System.Reflection.AssemblyName.GetAssemblyName(this.assemblyPath); + } + } + if(null == resolvedAssemblyName) + { + resolvedAssemblyName = assemblyName; + } + } + + /// + /// Checks if an assembly is already added. The method parses all references and compares the full assemblynames, or the location of the assemblies to decide whether two assemblies are the same. + /// + /// true if the assembly has already been added. + protected override bool IsAlreadyAdded() + { + ReferenceContainerNode referencesFolder = this.ProjectMgr.FindChild(ReferenceContainerNode.ReferencesNodeVirtualName) as ReferenceContainerNode; + Debug.Assert(referencesFolder != null, "Could not find the References node"); + bool shouldCheckPath = !string.IsNullOrEmpty(this.Url); + + for(HierarchyNode n = referencesFolder.FirstChild; n != null; n = n.NextSibling) + { + AssemblyReferenceNode assemblyRefererenceNode = n as AssemblyReferenceNode; + if(null != assemblyRefererenceNode) + { + // We will check if the full assemblynames are the same or if the Url of the assemblies is the same. + if(String.Compare(assemblyRefererenceNode.AssemblyName.FullName, this.assemblyName.FullName, StringComparison.OrdinalIgnoreCase) == 0 || + (shouldCheckPath && NativeMethods.IsSamePath(assemblyRefererenceNode.Url, this.Url))) + { + return true; + } + } + } + + return false; + } + + /// + /// Determines if this is node a valid node for painting the default reference icon. + /// + /// + protected override bool CanShowDefaultIcon() + { + if(String.IsNullOrEmpty(this.assemblyPath) || !File.Exists(this.assemblyPath)) + { + return false; + } + + return true; + } + + private void GetPathNameFromProjectFile() + { + string result = this.ItemNode.GetMetadata(ProjectFileConstants.HintPath); + if(String.IsNullOrEmpty(result)) + { + result = this.ItemNode.GetMetadata(ProjectFileConstants.AssemblyName); + if(String.IsNullOrEmpty(result)) + { + this.assemblyPath = String.Empty; + } + else if(!result.EndsWith(".dll", StringComparison.OrdinalIgnoreCase)) + { + result += ".dll"; + this.assemblyPath = result; + } + } + else + { + this.assemblyPath = this.GetFullPathFromPath(result); + } + } + + private string GetFullPathFromPath(string path) + { + if(Path.IsPathRooted(path)) + { + return path; + } + else + { + Uri uri = new Uri(this.ProjectMgr.BaseURI.Uri, path); + + if(uri != null) + { + return Microsoft.VisualStudio.Shell.Url.Unescape(uri.LocalPath, true); + } + } + + return String.Empty; + } + + protected override void ResolveReference() + { + this.ResolveAssemblyReference(); + } + + private void SetHintPathAndPrivateValue() + { + + // Private means local copy; we want to know if it is already set to not override the default + string privateValue = this.ItemNode.GetMetadata(ProjectFileConstants.Private); + + // Get the list of items which require HintPath + IEnumerable references = this.ProjectMgr.BuildProject.GetItems(MsBuildGeneratedItemType.ReferenceCopyLocalPaths); + + // Remove the HintPath, we will re-add it below if it is needed + if(!String.IsNullOrEmpty(this.assemblyPath)) + { + this.ItemNode.SetMetadata(ProjectFileConstants.HintPath, null); + } + + // Now loop through the generated References to find the corresponding one + foreach (MSBuild.ProjectItem reference in references) + { + string fileName = Path.GetFileNameWithoutExtension(reference.EvaluatedInclude); + if(String.Compare(fileName, this.assemblyName.Name, StringComparison.OrdinalIgnoreCase) == 0) + { + // We found it, now set some properties based on this. + + string hintPath = reference.GetMetadataValue(ProjectFileConstants.HintPath); + if(!String.IsNullOrEmpty(hintPath)) + { + if(Path.IsPathRooted(hintPath)) + { + hintPath = PackageUtilities.GetPathDistance(this.ProjectMgr.BaseURI.Uri, new Uri(hintPath)); + } + + this.ItemNode.SetMetadata(ProjectFileConstants.HintPath, hintPath); + // If this is not already set, we default to true + if(String.IsNullOrEmpty(privateValue)) + { + this.ItemNode.SetMetadata(ProjectFileConstants.Private, true.ToString()); + } + } + break; + } + + } + + } + + /// + /// This function ensures that some properies of the reference are set. + /// + private void SetReferenceProperties() + { + // Set a default HintPath for msbuild to be able to resolve the reference. + this.ItemNode.SetMetadata(ProjectFileConstants.HintPath, this.assemblyPath); + + // Resolve assembly referernces. This is needed to make sure that properties like the full path + // to the assembly or the hint path are set. + if(this.ProjectMgr.Build(MsBuildTarget.ResolveAssemblyReferences) != MSBuildResult.Successful) + { + return; + } + + // Check if we have to resolve again the path to the assembly. + if(string.IsNullOrEmpty(this.assemblyPath)) + { + ResolveReference(); + } + + // Make sure that the hint path if set (if needed). + SetHintPathAndPrivateValue(); + } + + /// + /// Does the actual job of resolving an assembly reference. We need a private method that does not violate + /// calling virtual method from the constructor. + /// + private void ResolveAssemblyReference() + { + if (this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return; + } + + var group = this.ProjectMgr.CurrentConfig.GetItems(ProjectFileConstants.ReferencePath); + foreach (var item in group) + { + string fullPath = this.GetFullPathFromPath(item.EvaluatedInclude); + + System.Reflection.AssemblyName name = System.Reflection.AssemblyName.GetAssemblyName(fullPath); + + // Try with full assembly name and then with weak assembly name. + if (String.Equals(name.FullName, this.assemblyName.FullName, StringComparison.OrdinalIgnoreCase) || String.Equals(name.Name, this.assemblyName.Name, StringComparison.OrdinalIgnoreCase)) + { + if (!NativeMethods.IsSamePath(fullPath, this.assemblyPath)) + { + // set the full path now. + this.assemblyPath = fullPath; + + // We have a new item to listen too, since the assembly reference is resolved from a different place. + this.fileChangeListener.ObserveItem(this.assemblyPath); + } + + this.resolvedAssemblyName = name; + + // No hint path is needed since the assembly path will always be resolved. + return; + } + } + } + + /// + /// Registers with File change events + /// + private void InitializeFileChangeEvents() + { + this.fileChangeListener = new FileChangeManager(this.ProjectMgr.Site); + this.fileChangeListener.FileChangedOnDisk += this.OnAssemblyReferenceChangedOnDisk; + } + + /// + /// Unregisters this node from file change notifications. + /// + private void UnregisterFromFileChangeService() + { + this.fileChangeListener.FileChangedOnDisk -= this.OnAssemblyReferenceChangedOnDisk; + this.fileChangeListener.Dispose(); + } + + /// + /// Event callback. Called when one of the assembly file is changed. + /// + /// The FileChangeManager object. + /// Event args containing the file name that was updated. + private void OnAssemblyReferenceChangedOnDisk(object sender, FileChangedOnDiskEventArgs e) + { + Debug.Assert(e != null, "No event args specified for the FileChangedOnDisk event"); + + // We only care about file deletes, so check for one before enumerating references. + if((e.FileChangeFlag & _VSFILECHANGEFLAGS.VSFILECHG_Del) == 0) + { + return; + } + + + if(NativeMethods.IsSamePath(e.FileName, this.assemblyPath)) + { + this.OnInvalidateItems(this.Parent); + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Attributes.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Attributes.cs new file mode 100644 index 0000000000..63b4d5d44e --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Attributes.cs @@ -0,0 +1,82 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Globalization; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Defines a type converter. + /// + /// This is needed to get rid of the type TypeConverter type that could not give back the Type we were passing to him. + /// We do not want to use reflection to get the type back from the ConverterTypeName. Also the GetType methos does not spwan converters from other assemblies. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1019:DefineAccessorsForAttributeArguments"), AttributeUsage(AttributeTargets.Class | AttributeTargets.Enum | AttributeTargets.Property | AttributeTargets.Field)] + public sealed class PropertyPageTypeConverterAttribute : Attribute + { + #region fields + Type converterType; + #endregion + + #region ctors + public PropertyPageTypeConverterAttribute(Type type) + { + this.converterType = type; + } + #endregion + + #region properties + public Type ConverterType + { + get + { + return this.converterType; + } + } + #endregion + } + + [AttributeUsage(AttributeTargets.Class | AttributeTargets.Property | AttributeTargets.Field, Inherited = false, AllowMultiple = false)] + internal sealed class LocDisplayNameAttribute : DisplayNameAttribute + { + #region fields + string name; + #endregion + + #region ctors + public LocDisplayNameAttribute(string name) + { + this.name = name; + } + #endregion + + #region properties + public override string DisplayName + { + get + { + string result = SR.GetString(this.name, CultureInfo.CurrentUICulture); + if(result == null) + { + Debug.Assert(false, "String resource '" + this.name + "' is missing"); + result = this.name; + } + return result; + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/AutomationScope.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/AutomationScope.cs new file mode 100644 index 0000000000..019330bb3c --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/AutomationScope.cs @@ -0,0 +1,110 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio.Shell.Interop; +using ErrorHandler = Microsoft.VisualStudio.ErrorHandler; + +namespace Microsoft.VisualStudio.Project.Automation +{ + /// + /// Helper class that handle the scope of an automation function. + /// It should be used inside a "using" directive to define the scope of the + /// automation function and make sure that the ExitAutomation method is called. + /// + internal class AutomationScope : IDisposable + { + private IVsExtensibility3 extensibility; + private bool inAutomation; + private static volatile object Mutex; + private bool isDisposed; + + /// + /// Initializes the class. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + static AutomationScope() + { + Mutex = new object(); + } + + /// + /// Defines the beginning of the scope of an automation function. This constuctor + /// calls EnterAutomationFunction to signal the Shell that the current function is + /// changing the status of the automation objects. + /// + public AutomationScope(IServiceProvider provider) + { + if(null == provider) + { + throw new ArgumentNullException("provider"); + } + extensibility = provider.GetService(typeof(EnvDTE.IVsExtensibility)) as IVsExtensibility3; + if(null == extensibility) + { + throw new InvalidOperationException(); + } + ErrorHandler.ThrowOnFailure(extensibility.EnterAutomationFunction()); + inAutomation = true; + } + + /// + /// Ends the scope of the automation function. This function is also called by the + /// Dispose method. + /// + public void ExitAutomation() + { + if(inAutomation) + { + ErrorHandler.ThrowOnFailure(extensibility.ExitAutomationFunction()); + inAutomation = false; + } + } + + /// + /// Gets the IVsExtensibility3 interface used in the automation function. + /// + public IVsExtensibility3 Extensibility + { + get { return extensibility; } + } + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + #region IDisposable Members + private void Dispose(bool disposing) + { + if(!this.isDisposed) + { + lock(Mutex) + { + if(disposing) + { + ExitAutomation(); + } + + this.isDisposed = true; + } + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAFileItem.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAFileItem.cs new file mode 100644 index 0000000000..7ae70bacc5 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAFileItem.cs @@ -0,0 +1,376 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using EnvDTE; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using IServiceProvider = System.IServiceProvider; +using VSConstants = Microsoft.VisualStudio.VSConstants; + +namespace Microsoft.VisualStudio.Project.Automation +{ + /// + /// Represents an automation object for a file in a project + /// + [SuppressMessage("Microsoft.Interoperability", "CA1405:ComVisibleTypeBaseTypesShouldBeComVisible")] + [ComVisible(true), CLSCompliant(false)] + public class OAFileItem : OAProjectItem + { + #region ctors + public OAFileItem(OAProject project, FileNode node) + : base(project, node) + { + } + + #endregion + + #region overridden methods + /// + /// Returns the dirty state of the document. + /// + /// Is thrown if the project is closed or it the service provider attached to the project is invalid. + /// Is thrown if the dirty state cannot be retrived. + public override bool IsDirty + { + get + { + if(this.Node == null || this.Node.ProjectMgr == null || this.Node.ProjectMgr.IsClosed || this.Node.ProjectMgr.Site == null) + { + throw new InvalidOperationException(); + } + bool isDirty = false; + + using(AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) + { + DocumentManager manager = this.Node.GetDocumentManager(); + + if(manager == null) + { + throw new InvalidOperationException(); + } + + bool isOpen, isOpenedByUs; + uint docCookie; + IVsPersistDocData persistDocData; + manager.GetDocInfo(out isOpen, out isDirty, out isOpenedByUs, out docCookie, out persistDocData); + } + return isDirty; + } + + } + + /// + /// Gets the Document associated with the item, if one exists. + /// + public override EnvDTE.Document Document + { + get + { + if(this.Node == null || this.Node.ProjectMgr == null || this.Node.ProjectMgr.IsClosed || this.Node.ProjectMgr.Site == null) + { + throw new InvalidOperationException(); + } + + EnvDTE.Document document = null; + + using(AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) + { + IVsUIHierarchy hier; + uint itemid; + + IVsWindowFrame windowFrame; + + VsShellUtilities.IsDocumentOpen(this.Node.ProjectMgr.Site, this.Node.Url, VSConstants.LOGVIEWID_Any, out hier, out itemid, out windowFrame); + + if(windowFrame != null) + { + object var; + ErrorHandler.ThrowOnFailure(windowFrame.GetProperty((int)__VSFPROPID.VSFPROPID_DocCookie, out var)); + object documentAsObject; + ErrorHandler.ThrowOnFailure(scope.Extensibility.GetDocumentFromDocCookie((int)var, out documentAsObject)); + if(documentAsObject == null) + { + throw new InvalidOperationException(); + } + else + { + document = (Document)documentAsObject; + } + } + + } + + return document; + } + } + + + /// + /// Opens the file item in the specified view. + /// + /// Specifies the view kind in which to open the item (file) + /// Window object + public override EnvDTE.Window Open(string viewKind) + { + if(this.Node == null || this.Node.ProjectMgr == null || this.Node.ProjectMgr.IsClosed || this.Node.ProjectMgr.Site == null) + { + throw new InvalidOperationException(); + } + + IVsWindowFrame windowFrame = null; + IntPtr docData = IntPtr.Zero; + + using(AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) + { + try + { + // Validate input params + Guid logicalViewGuid = VSConstants.LOGVIEWID_Primary; + try + { + if(!(String.IsNullOrEmpty(viewKind))) + { + logicalViewGuid = new Guid(viewKind); + } + } + catch(FormatException) + { + // Not a valid guid + throw new ArgumentException(SR.GetString(SR.ParameterMustBeAValidGuid, CultureInfo.CurrentUICulture), "viewKind"); + } + + uint itemid; + IVsHierarchy ivsHierarchy; + uint docCookie; + IVsRunningDocumentTable rdt = this.Node.ProjectMgr.Site.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + Debug.Assert(rdt != null, " Could not get running document table from the services exposed by this project"); + if(rdt == null) + { + throw new InvalidOperationException(); + } + + ErrorHandler.ThrowOnFailure(rdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, this.Node.Url, out ivsHierarchy, out itemid, out docData, out docCookie)); + + // Open the file using the IVsProject3 interface + ErrorHandler.ThrowOnFailure(this.Node.ProjectMgr.OpenItem(this.Node.ID, ref logicalViewGuid, docData, out windowFrame)); + + } + finally + { + if(docData != IntPtr.Zero) + { + Marshal.Release(docData); + } + } + } + + // Get the automation object and return it + return ((windowFrame != null) ? VsShellUtilities.GetWindowObject(windowFrame) : null); + } + + /// + /// Saves the project item. + /// + /// The name with which to save the project or project item. + /// Is thrown if the save operation failes. + /// Is thrown if fileName is null. + public override void Save(string fileName) + { + this.DoSave(false, fileName); + } + + /// + /// Saves the project item. + /// + /// The file name with which to save the solution, project, or project item. If the file exists, it is overwritten + /// true if the rename was successful. False if Save as failes + public override bool SaveAs(string fileName) + { + try + { + this.DoSave(true, fileName); + } + catch(InvalidOperationException) + { + return false; + } + catch(COMException) + { + return false; + } + return true; + } + + /// + /// Gets a value indicating whether the project item is open in a particular view type. + /// + /// A Constants.vsViewKind* indicating the type of view to check./param> + /// A Boolean value indicating true if the project is open in the given view type; false if not. + public override bool get_IsOpen(string viewKind) + { + if(this.Node == null || this.Node.ProjectMgr == null || this.Node.ProjectMgr.IsClosed || this.Node.ProjectMgr.Site == null) + { + throw new InvalidOperationException(); + } + + // Validate input params + Guid logicalViewGuid = VSConstants.LOGVIEWID_Primary; + try + { + if(!(String.IsNullOrEmpty(viewKind))) + { + logicalViewGuid = new Guid(viewKind); + } + } + catch(FormatException) + { + // Not a valid guid + throw new ArgumentException(SR.GetString(SR.ParameterMustBeAValidGuid, CultureInfo.CurrentUICulture), "viewKind"); + } + + bool isOpen = false; + + using(AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) + { + IVsUIHierarchy hier; + uint itemid; + + IVsWindowFrame windowFrame; + + isOpen = VsShellUtilities.IsDocumentOpen(this.Node.ProjectMgr.Site, this.Node.Url, logicalViewGuid, out hier, out itemid, out windowFrame); + + } + + return isOpen; + } + + /// + /// Gets the ProjectItems for the object. + /// + public override ProjectItems ProjectItems + { + get + { + if(this.Project.Project.CanFileNodesHaveChilds) + return new OAProjectItems(this.Project, this.Node); + else + return base.ProjectItems; + } + } + + + #endregion + + #region helpers + /// + /// Saves or Save As the file + /// + /// Flag determining which Save method called , the SaveAs or the Save. + /// The name of the project file. + private void DoSave(bool isCalledFromSaveAs, string fileName) + { + if(fileName == null) + { + throw new ArgumentNullException("fileName"); + } + + if(this.Node == null || this.Node.ProjectMgr == null || this.Node.ProjectMgr.IsClosed || this.Node.ProjectMgr.Site == null) + { + throw new InvalidOperationException(); + } + + using(AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) + { + IntPtr docData = IntPtr.Zero; + + try + { + IVsRunningDocumentTable rdt = this.Node.ProjectMgr.Site.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + Debug.Assert(rdt != null, " Could not get running document table from the services exposed by this project"); + if(rdt == null) + { + throw new InvalidOperationException(); + } + + // First we see if someone else has opened the requested view of the file. + uint itemid; + IVsHierarchy ivsHierarchy; + uint docCookie; + int canceled; + string url = this.Node.Url; + + ErrorHandler.ThrowOnFailure(rdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, url, out ivsHierarchy, out itemid, out docData, out docCookie)); + + // If an empty file name is passed in for Save then make the file name the project name. + if(!isCalledFromSaveAs && fileName.Length == 0) + { + ErrorHandler.ThrowOnFailure(this.Node.ProjectMgr.SaveItem(VSSAVEFLAGS.VSSAVE_SilentSave, url, this.Node.ID, docData, out canceled)); + } + else + { + Utilities.ValidateFileName(this.Node.ProjectMgr.Site, fileName); + + // Compute the fullpath from the directory of the existing Url. + string fullPath = fileName; + if(!Path.IsPathRooted(fileName)) + { + string directory = Path.GetDirectoryName(url); + fullPath = Path.Combine(directory, fileName); + } + + if(!isCalledFromSaveAs) + { + if(!NativeMethods.IsSamePath(this.Node.Url, fullPath)) + { + throw new InvalidOperationException(); + } + + ErrorHandler.ThrowOnFailure(this.Node.ProjectMgr.SaveItem(VSSAVEFLAGS.VSSAVE_SilentSave, fullPath, this.Node.ID, docData, out canceled)); + } + else + { + ErrorHandler.ThrowOnFailure(this.Node.ProjectMgr.SaveItem(VSSAVEFLAGS.VSSAVE_SilentSave, fullPath, this.Node.ID, docData, out canceled)); + } + } + + if(canceled == 1) + { + throw new InvalidOperationException(); + } + } + catch(COMException e) + { + throw new InvalidOperationException(e.Message); + } + finally + { + if(docData != IntPtr.Zero) + { + Marshal.Release(docData); + } + } + } + + } + #endregion + + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAFolderItem.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAFolderItem.cs new file mode 100644 index 0000000000..3064c8fb7d --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAFolderItem.cs @@ -0,0 +1,56 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using EnvDTE; + +namespace Microsoft.VisualStudio.Project.Automation +{ + /// + /// Represents an automation object for a folder in a project + /// + [SuppressMessage("Microsoft.Interoperability", "CA1405:ComVisibleTypeBaseTypesShouldBeComVisible")] + [ComVisible(true), CLSCompliant(false)] + public class OAFolderItem : OAProjectItem + { + #region ctors + public OAFolderItem(OAProject project, FolderNode node) + : base(project, node) + { + } + + #endregion + + #region overridden methods + public override ProjectItems Collection + { + get + { + ProjectItems items = new OAProjectItems(this.Project, this.Node); + return items; + } + } + + public override ProjectItems ProjectItems + { + get + { + return this.Collection; + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OANavigableProjectItems.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OANavigableProjectItems.cs new file mode 100644 index 0000000000..176f0e8826 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OANavigableProjectItems.cs @@ -0,0 +1,280 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace Microsoft.VisualStudio.Project.Automation +{ + /// + /// This can navigate a collection object only (partial implementation of ProjectItems interface) + /// + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] + [ComVisible(true), CLSCompliant(false)] + public class OANavigableProjectItems : EnvDTE.ProjectItems + { + #region fields + private OAProject project; + private IList items; + private HierarchyNode nodeWithItems; + #endregion + + #region properties + /// + /// Defines an internal list of project items + /// + internal IList Items + { + get + { + return this.items; + } + } + + /// + /// Defines a relationship to the associated project. + /// + internal OAProject Project + { + get + { + return this.project; + } + } + + /// + /// Defines the node that contains the items + /// + internal HierarchyNode NodeWithItems + { + get + { + return this.nodeWithItems; + } + } + #endregion + + #region ctor + /// + /// Constructor. + /// + /// The associated project. + /// The node that defines the items. + public OANavigableProjectItems(OAProject project, HierarchyNode nodeWithItems) + { + this.project = project; + this.nodeWithItems = nodeWithItems; + this.items = this.GetListOfProjectItems(); + } + + /// + /// Constructor. + /// + /// The associated project. + /// A list of items that will make up the items defined by this object. + /// The node that defines the items. + public OANavigableProjectItems(OAProject project, IList items, HierarchyNode nodeWithItems) + { + this.items = items; + this.project = project; + this.nodeWithItems = nodeWithItems; + } + #endregion + + #region EnvDTE.ProjectItems + + /// + /// Gets a value indicating the number of objects in the collection. + /// + public virtual int Count + { + get + { + return items.Count; + } + } + + /// + /// Gets the immediate parent object of a ProjectItems collection. + /// + public virtual object Parent + { + get + { + return this.nodeWithItems.GetAutomationObject(); + } + } + + /// + /// Gets an enumeration indicating the type of object. + /// + public virtual string Kind + { + get + { + // TODO: Add OAProjectItems.Kind getter implementation + return null; + } + } + + /// + /// Gets the top-level extensibility object. + /// + public virtual EnvDTE.DTE DTE + { + get + { + return (EnvDTE.DTE)this.project.DTE; + } + } + + /// + /// Gets the project hosting the project item or items. + /// + public virtual EnvDTE.Project ContainingProject + { + get + { + return this.project; + } + } + + /// + /// Adds one or more ProjectItem objects from a directory to the ProjectItems collection. + /// + /// The directory from which to add the project item. + /// A ProjectItem object. + public virtual EnvDTE.ProjectItem AddFromDirectory(string directory) + { + throw new NotImplementedException(); + } + + /// + /// Creates a new project item from an existing item template file and adds it to the project. + /// + /// The full path and file name of the template project file. + /// The file name to use for the new project item. + /// A ProjectItem object. + public virtual EnvDTE.ProjectItem AddFromTemplate(string fileName, string name) + { + throw new NotImplementedException(); + } + + /// + /// Creates a new folder in Solution Explorer. + /// + /// The name of the folder node in Solution Explorer. + /// The type of folder to add. The available values are based on vsProjectItemsKindConstants and vsProjectItemKindConstants + /// A ProjectItem object. + public virtual EnvDTE.ProjectItem AddFolder(string name, string kind) + { + throw new NotImplementedException(); + } + + /// + /// Copies a source file and adds it to the project. + /// + /// The path and file name of the project item to be added. + /// A ProjectItem object. + public virtual EnvDTE.ProjectItem AddFromFileCopy(string filePath) + { + throw new NotImplementedException(); + } + + /// + /// Adds a project item from a file that is installed in a project directory structure. + /// + /// The file name of the item to add as a project item. + /// A ProjectItem object. + public virtual EnvDTE.ProjectItem AddFromFile(string fileName) + { + throw new NotImplementedException(); + } + + /// + /// Get Project Item from index + /// + /// Either index by number (1-based) or by name can be used to get the item + /// Project Item. ArgumentException if invalid index is specified + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + public virtual EnvDTE.ProjectItem Item(object index) + { + // Changed from MPFProj: throws ArgumentException instead of returning null (http://mpfproj10.codeplex.com/workitem/9158) + if(index is int) + { + int realIndex = (int)index - 1; + if(realIndex >= 0 && realIndex < this.items.Count) + { + return (EnvDTE.ProjectItem)items[realIndex]; + } + } + else if(index is string) + { + string name = (string)index; + foreach(EnvDTE.ProjectItem item in items) + { + if(String.Compare(item.Name, name, StringComparison.OrdinalIgnoreCase) == 0) + { + return item; + } + } + } + throw new ArgumentException(); + } + + /// + /// Returns an enumeration for items in a collection. + /// + /// An IEnumerator for this object. + public virtual IEnumerator GetEnumerator() + { + if(this.items == null) + { + yield return null; + } + + int count = items.Count; + for(int i = 0; i < count; i++) + { + yield return items[i]; + } + } + + #endregion + + #region virtual methods + /// + /// Retrives a list of items associated with the current node. + /// + /// A List of project items + protected IList GetListOfProjectItems() + { + List list = new List(); + for(HierarchyNode child = this.NodeWithItems.FirstChild; child != null; child = child.NextSibling) + { + EnvDTE.ProjectItem item = child.GetAutomationObject() as EnvDTE.ProjectItem; + if(null != item) + { + list.Add(item); + } + } + + return list; + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OANestedProjectItem.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OANestedProjectItem.cs new file mode 100644 index 0000000000..2575c412c7 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OANestedProjectItem.cs @@ -0,0 +1,77 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.VisualStudio.Project.Automation +{ + [SuppressMessage("Microsoft.Interoperability", "CA1405:ComVisibleTypeBaseTypesShouldBeComVisible")] + [ComVisible(true), CLSCompliant(false)] + public class OANestedProjectItem : OAProjectItem + { + #region fields + EnvDTE.Project nestedProject; + #endregion + + #region ctors + public OANestedProjectItem(OAProject project, NestedProjectNode node) + : base(project, node) + { + if (node == null) + { + throw new ArgumentNullException("node"); + } + + object nestedproject; + if(ErrorHandler.Succeeded(node.NestedHierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ExtObject, out nestedproject))) + { + this.nestedProject = nestedproject as EnvDTE.Project; + } + } + + #endregion + + #region overridden methods + /// + /// Returns the collection of project items defined in the nested project + /// + public override EnvDTE.ProjectItems ProjectItems + { + get + { + if(this.nestedProject != null) + { + return this.nestedProject.ProjectItems; + } + return null; + } + } + + /// + /// Returns the nested project. + /// + public override EnvDTE.Project SubProject + { + get + { + return this.nestedProject; + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OANullProperty.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OANullProperty.cs new file mode 100644 index 0000000000..66767480ae --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OANullProperty.cs @@ -0,0 +1,104 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; + +namespace Microsoft.VisualStudio.Project.Automation +{ + /// + /// This object defines a so called null object that is returned as instead of null. This is because callers in VSCore usually crash if a null propery is returned for them. + /// + [CLSCompliant(false), ComVisible(true)] + public class OANullProperty : EnvDTE.Property + { + #region fields + private OAProperties parent; + #endregion + + #region ctors + + public OANullProperty(OAProperties parent) + { + this.parent = parent; + } + #endregion + + #region EnvDTE.Property + + public object Application + { + get { return String.Empty; } + } + + public EnvDTE.Properties Collection + { + get + { + //todo: EnvDTE.Property.Collection + return this.parent; + } + } + + public EnvDTE.DTE DTE + { + get { return null; } + } + + public object get_IndexedValue(object index1, object index2, object index3, object index4) + { + return String.Empty; + } + + public void let_Value(object value) + { + //todo: let_Value + } + + public string Name + { + get { return String.Empty; } + } + + public short NumIndices + { + get { return 0; } + } + + public object Object + { + get { return this.parent.Target; } + set + { + } + } + + public EnvDTE.Properties Parent + { + get { return this.parent; } + } + + public void set_IndexedValue(object index1, object index2, object index3, object index4, object value) + { + + } + + public object Value + { + get { return String.Empty; } + set { } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAProject.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAProject.cs new file mode 100644 index 0000000000..49a68a73cb --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAProject.cs @@ -0,0 +1,474 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using EnvDTE; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.VisualStudio.Project.Automation +{ + [ComVisible(true), CLSCompliant(false)] + public class OAProject : EnvDTE.Project, EnvDTE.ISupportVSProperties + { + #region fields + private ProjectNode project; + EnvDTE.ConfigurationManager configurationManager; + #endregion + + #region properties + public ProjectNode Project + { + get { return this.project; } + } + #endregion + + #region ctor + public OAProject(ProjectNode project) + { + this.project = project; + } + #endregion + + #region EnvDTE.Project + /// + /// Gets or sets the name of the object. + /// + public virtual string Name + { + get + { + return project.Caption; + } + set + { + if(this.project == null || this.project.Site == null || this.project.IsClosed) + { + throw new InvalidOperationException(); + } + + using(AutomationScope scope = new AutomationScope(this.project.Site)) + { + project.SetEditLabel(value); + } + } + } + + /// + /// Microsoft Internal Use Only. Gets the file name of the project. + /// + public virtual string FileName + { + get + { + return project.ProjectFile; + } + } + + /// + /// Microsoft Internal Use Only. Specfies if the project is dirty. + /// + public virtual bool IsDirty + { + get + { + int dirty; + + ErrorHandler.ThrowOnFailure(project.IsDirty(out dirty)); + return dirty != 0; + } + set + { + if(this.project == null || this.project.Site == null || this.project.IsClosed) + { + throw new InvalidOperationException(); + } + + using(AutomationScope scope = new AutomationScope(this.project.Site)) + { + project.SetProjectFileDirty(value); + } + } + } + + /// + /// Gets the Projects collection containing the Project object supporting this property. + /// + public virtual EnvDTE.Projects Collection + { + get { return null; } + } + + /// + /// Gets the top-level extensibility object. + /// + public virtual EnvDTE.DTE DTE + { + get + { + return (EnvDTE.DTE)this.project.Site.GetService(typeof(EnvDTE.DTE)); + } + } + + /// + /// Gets a GUID string indicating the kind or type of the object. + /// + public virtual string Kind + { + get { return project.ProjectGuid.ToString("B"); } + } + + /// + /// Gets a ProjectItems collection for the Project object. + /// + public virtual EnvDTE.ProjectItems ProjectItems + { + get + { + return new OAProjectItems(this, project); + } + } + + /// + /// Gets a collection of all properties that pertain to the Project object. + /// + public virtual EnvDTE.Properties Properties + { + get + { + return new OAProperties(this.project.NodeProperties); + } + } + + /// + /// Returns the name of project as a relative path from the directory containing the solution file to the project file + /// + /// Unique name if project is in a valid state. Otherwise null + public virtual string UniqueName + { + get + { + if(this.project == null || this.project.IsClosed) + { + return null; + } + else + { + // Get Solution service + IVsSolution solution = this.project.GetService(typeof(IVsSolution)) as IVsSolution; + if(solution == null) + { + throw new InvalidOperationException(); + } + + // Ask solution for unique name of project + string uniqueName = string.Empty; + ErrorHandler.ThrowOnFailure(solution.GetUniqueNameOfProject(this.project, out uniqueName)); + return uniqueName; + } + } + } + + /// + /// Gets an interface or object that can be accessed by name at run time. + /// + public virtual object Object + { + get { return this.project.Object; } + } + + /// + /// Gets the requested Extender object if it is available for this object. + /// + /// The name of the extender object. + /// An Extender object. + public virtual object get_Extender(string name) + { + return null; + } + + /// + /// Gets a list of available Extenders for the object. + /// + public virtual object ExtenderNames + { + get { return null; } + } + + /// + /// Gets the Extender category ID (CATID) for the object. + /// + public virtual string ExtenderCATID + { + get { return String.Empty; } + } + + /// + /// Gets the full path and name of the Project object's file. + /// + public virtual string FullName + { + get + { + string filename; + uint format; + ErrorHandler.ThrowOnFailure(project.GetCurFile(out filename, out format)); + return filename; + } + } + + /// + /// Gets or sets a value indicatingwhether the object has not been modified since last being saved or opened. + /// + public virtual bool Saved + { + get + { + return !this.IsDirty; + } + set + { + if(this.project == null || this.project.Site == null || this.project.IsClosed) + { + throw new InvalidOperationException(); + } + + using(AutomationScope scope = new AutomationScope(this.project.Site)) + { + project.SetProjectFileDirty(!value); + } + } + } + + /// + /// Gets the ConfigurationManager object for this Project . + /// + public virtual EnvDTE.ConfigurationManager ConfigurationManager + { + get + { + if(this.configurationManager == null) + { + IVsExtensibility3 extensibility = this.project.Site.GetService(typeof(IVsExtensibility)) as IVsExtensibility3; + + if(extensibility == null) + { + throw new InvalidOperationException(); + } + + object configurationManagerAsObject; + ErrorHandler.ThrowOnFailure(extensibility.GetConfigMgr(this.project, VSConstants.VSITEMID_ROOT, out configurationManagerAsObject)); + + if(configurationManagerAsObject == null) + { + throw new InvalidOperationException(); + } + else + { + this.configurationManager = (ConfigurationManager)configurationManagerAsObject; + } + } + + return this.configurationManager; + } + } + + /// + /// Gets the Globals object containing add-in values that may be saved in the solution (.sln) file, the project file, or in the user's profile data. + /// + public virtual EnvDTE.Globals Globals + { + get { return null; } + } + + /// + /// Gets a ProjectItem object for the nested project in the host project. + /// + public virtual EnvDTE.ProjectItem ParentProjectItem + { + get { return null; } + } + + /// + /// Gets the CodeModel object for the project. + /// + public virtual EnvDTE.CodeModel CodeModel + { + get { return null; } + } + + /// + /// Saves the project. + /// + /// The file name with which to save the solution, project, or project item. If the file exists, it is overwritten + /// Is thrown if the save operation failes. + /// Is thrown if fileName is null. + public virtual void SaveAs(string fileName) + { + this.DoSave(true, fileName); + } + + /// + /// Saves the project + /// + /// The file name of the project + /// Is thrown if the save operation failes. + /// Is thrown if fileName is null. + public virtual void Save(string fileName) + { + this.DoSave(false, fileName); + } + + /// + /// Removes the project from the current solution. + /// + public virtual void Delete() + { + if(this.project == null || this.project.Site == null || this.project.IsClosed) + { + throw new InvalidOperationException(); + } + + using(AutomationScope scope = new AutomationScope(this.project.Site)) + { + this.project.Remove(false); + } + } + #endregion + + #region ISupportVSProperties methods + /// + /// Microsoft Internal Use Only. + /// + public virtual void NotifyPropertiesDelete() + { + } + #endregion + + #region private methods + /// + /// Saves or Save Asthe project. + /// + /// Flag determining which Save method called , the SaveAs or the Save. + /// The name of the project file. + private void DoSave(bool isCalledFromSaveAs, string fileName) + { + if(fileName == null) + { + throw new ArgumentNullException("fileName"); + } + + if(this.project == null || this.project.Site == null || this.project.IsClosed) + { + throw new InvalidOperationException(); + } + + using(AutomationScope scope = new AutomationScope(this.project.Site)) + { + // If an empty file name is passed in for Save then make the file name the project name. + if(!isCalledFromSaveAs && string.IsNullOrEmpty(fileName)) + { + // Use the solution service to save the project file. Note that we have to use the service + // so that all the shell's elements are aware that we are inside a save operation and + // all the file change listenters registered by the shell are suspended. + + // Get the cookie of the project file from the RTD. + IVsRunningDocumentTable rdt = this.project.Site.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + if(null == rdt) + { + throw new InvalidOperationException(); + } + + IVsHierarchy hier; + uint itemid; + IntPtr unkData; + uint cookie; + ErrorHandler.ThrowOnFailure(rdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, this.project.Url, out hier, + out itemid, out unkData, out cookie)); + if(IntPtr.Zero != unkData) + { + Marshal.Release(unkData); + } + + // Verify that we have a cookie. + if(0 == cookie) + { + // This should never happen because if the project is open, then it must be in the RDT. + throw new InvalidOperationException(); + } + + // Get the IVsHierarchy for the project. + IVsHierarchy prjHierarchy = HierarchyNode.GetOuterHierarchy(this.project); + + // Now get the soulution. + IVsSolution solution = this.project.Site.GetService(typeof(SVsSolution)) as IVsSolution; + // Verify that we have both solution and hierarchy. + if((null == prjHierarchy) || (null == solution)) + { + throw new InvalidOperationException(); + } + + ErrorHandler.ThrowOnFailure(solution.SaveSolutionElement((uint)__VSSLNSAVEOPTIONS.SLNSAVEOPT_SaveIfDirty, prjHierarchy, cookie)); + } + else + { + + // We need to make some checks before we can call the save method on the project node. + // This is mainly because it is now us and not the caller like in case of SaveAs or Save that should validate the file name. + // The IPersistFileFormat.Save method only does a validation that is necesseray to be performed. Example: in case of Save As the + // file name itself is not validated only the whole path. (thus a file name like file\file is accepted, since as a path is valid) + + // 1. The file name has to be valid. + string fullPath = fileName; + try + { + if(!Path.IsPathRooted(fileName)) + { + fullPath = Path.Combine(this.project.ProjectFolder, fileName); + } + } + // We want to be consistent in the error message and exception we throw. fileName could be for example #¤&%"¤&"% and that would trigger an ArgumentException on Path.IsRooted. + catch(ArgumentException) + { + throw new InvalidOperationException(SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture)); + } + + // It might be redundant but we validate the file and the full path of the file being valid. The SaveAs would also validate the path. + // If we decide that this is performance critical then this should be refactored. + Utilities.ValidateFileName(this.project.Site, fullPath); + + if(!isCalledFromSaveAs) + { + // 2. The file name has to be the same + if(!NativeMethods.IsSamePath(fullPath, this.project.Url)) + { + throw new InvalidOperationException(); + } + + ErrorHandler.ThrowOnFailure(this.project.Save(fullPath, 1, 0)); + } + else + { + ErrorHandler.ThrowOnFailure(this.project.Save(fullPath, 0, 0)); + } + } + } + + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAProjectItem.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAProjectItem.cs new file mode 100644 index 0000000000..bc5966e4ef --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAProjectItem.cs @@ -0,0 +1,429 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.VisualStudio.Project.Automation +{ + [ComVisible(true), CLSCompliant(false)] + public class OAProjectItem : EnvDTE.ProjectItem + where T : HierarchyNode + { + + #region fields + private T node; + private OAProject project; + #endregion + + #region properties + protected T Node + { + get + { + return this.node; + } + } + + /// + /// Returns the automation project + /// + protected OAProject Project + { + get + { + return this.project; + } + } + #endregion + + #region ctors + public OAProjectItem(OAProject project, T node) + { + this.node = node; + this.project = project; + } + #endregion + + #region EnvDTE.ProjectItem + + /// + /// Gets the requested Extender if it is available for this object + /// + /// The name of the extender. + /// The extender object. + public virtual object get_Extender(string extenderName) + { + return null; + } + + /// + /// Gets an object that can be accessed by name at run time. + /// + public virtual object Object + { + get + { + return this.node.Object; + } + } + + /// + /// Gets the Document associated with the item, if one exists. + /// + public virtual EnvDTE.Document Document + { + get + { + return null; + } + } + + /// + /// Gets the number of files associated with a ProjectItem. + /// + public virtual short FileCount + { + get + { + return (short)1; + } + } + + /// + /// Gets a collection of all properties that pertain to the object. + /// + public virtual EnvDTE.Properties Properties + { + get + { + if(this.node.NodeProperties == null) + { + return null; + } + return new OAProperties(this.node.NodeProperties); + } + } + + + /// + /// Gets the FileCodeModel object for the project item. + /// + public virtual EnvDTE.FileCodeModel FileCodeModel + { + get + { + return null; + } + } + + /// + /// Gets a ProjectItems for the object. + /// + public virtual EnvDTE.ProjectItems ProjectItems + { + get + { + return null; + } + } + + /// + /// Gets a GUID string indicating the kind or type of the object. + /// + public virtual string Kind + { + get + { + Guid guid; + ErrorHandler.ThrowOnFailure(this.node.GetGuidProperty((int)__VSHPROPID.VSHPROPID_TypeGuid, out guid)); + return guid.ToString("B"); + } + } + + /// + /// Saves the project item. + /// + /// The name with which to save the project or project item. + /// Implemented by subclasses. + public virtual void Save(string fileName) + { + throw new NotImplementedException(); + } + + /// + /// Gets the top-level extensibility object. + /// + public virtual EnvDTE.DTE DTE + { + get + { + return (EnvDTE.DTE)this.project.DTE; + } + } + + /// + /// Gets the ProjectItems collection containing the ProjectItem object supporting this property. + /// + public virtual EnvDTE.ProjectItems Collection + { + get + { + // Get the parent node + HierarchyNode parentNode = this.node.Parent; + Debug.Assert(parentNode != null, "Failed to get the parent node"); + + // Get the ProjectItems object for the parent node + if(parentNode is ProjectNode) + { + // The root node for the project + return ((OAProject)parentNode.GetAutomationObject()).ProjectItems; + } + else if(parentNode is FileNode && parentNode.FirstChild != null) + { + // The item has children + return ((OAProjectItem)parentNode.GetAutomationObject()).ProjectItems; + } + else if(parentNode is FolderNode) + { + return ((OAProjectItem)parentNode.GetAutomationObject()).ProjectItems; + } + else + { + // Not supported. Override this method in derived classes to return appropriate collection object + throw new NotImplementedException(); + } + } + } + /// + /// Gets a list of available Extenders for the object. + /// + public virtual object ExtenderNames + { + get + { + return null; + } + } + + /// + /// Gets the ConfigurationManager object for this ProjectItem. + /// + /// We do not support config management based per item. + public virtual EnvDTE.ConfigurationManager ConfigurationManager + { + get + { + return null; + } + } + + /// + /// Gets the project hosting the ProjectItem. + /// + public virtual EnvDTE.Project ContainingProject + { + get + { + return this.project; + } + } + + /// + /// Gets or sets a value indicating whether or not the object has been modified since last being saved or opened. + /// + public virtual bool Saved + { + get + { + return !this.IsDirty; + + } + set + { + throw new NotImplementedException(); + } + } + + /// + /// Gets the Extender category ID (CATID) for the object. + /// + public virtual string ExtenderCATID + { + get + { + return null; + } + } + + /// + /// If the project item is the root of a subproject, then the SubProject property returns the Project object for the subproject. + /// + public virtual EnvDTE.Project SubProject + { + get + { + return null; + } + } + + /// + /// Microsoft Internal Use Only. Checks if the document associated to this item is dirty. + /// + public virtual bool IsDirty + { + get + { + throw new NotImplementedException(); + } + set + { + throw new NotImplementedException(); + } + } + + /// + /// Gets or sets the name of the object. + /// + public virtual string Name + { + get + { + return this.node.Caption; + } + set + { + if(this.node == null || this.node.ProjectMgr == null || this.node.ProjectMgr.IsClosed || this.node.ProjectMgr.Site == null) + { + throw new InvalidOperationException(); + } + + using(AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) + { + this.node.SetEditLabel(value); + } + } + } + /// + /// Removes the project item from hierarchy. + /// + public virtual void Remove() + { + if(this.node == null || this.node.ProjectMgr == null || this.node.ProjectMgr.IsClosed || this.node.ProjectMgr.Site == null) + { + throw new InvalidOperationException(); + } + + using(AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) + { + this.node.Remove(false); + } + } + + /// + /// Removes the item from its project and its storage. + /// + public virtual void Delete() + { + if(this.node == null || this.node.ProjectMgr == null || this.node.ProjectMgr.IsClosed || this.node.ProjectMgr.Site == null) + { + throw new InvalidOperationException(); + } + + using(AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) + { + this.node.Remove(true); + } + } + + /// + /// Saves the project item. + /// + /// The file name with which to save the solution, project, or project item. If the file exists, it is overwritten. + /// true if save was successful + /// This method is implemented on subclasses. + public virtual bool SaveAs(string newFileName) + { + throw new NotImplementedException(); + } + + /// + /// Gets a value indicating whether the project item is open in a particular view type. + /// + /// A Constants.vsViewKind* indicating the type of view to check./param> + /// A Boolean value indicating true if the project is open in the given view type; false if not. + public virtual bool get_IsOpen(string viewKind) + { + throw new NotImplementedException(); + } + + /// + /// Gets the full path and names of the files associated with a project item. + /// + /// The index of the item + /// The full path of the associated item + /// Is thrown if index is not one + public virtual string get_FileNames(short index) + { + // This method should really only be called with 1 as the parameter, but + // there used to be a bug in VB/C# that would work with 0. To avoid breaking + // existing automation they are still accepting 0. To be compatible with them + // we accept it as well. + Debug.Assert(index > 0, "Index is 1 based."); + if(index < 0) + { + throw new ArgumentOutOfRangeException("index"); + } + return this.node.Url; + } + + /// + /// Expands the view of Solution Explorer to show project items. + /// + public virtual void ExpandView() + { + if(this.node == null || this.node.ProjectMgr == null || this.node.ProjectMgr.IsClosed || this.node.ProjectMgr.Site == null) + { + throw new InvalidOperationException(); + } + + using(AutomationScope scope = new AutomationScope(this.Node.ProjectMgr.Site)) + { + IVsUIHierarchyWindow uiHierarchy = UIHierarchyUtilities.GetUIHierarchyWindow(this.node.ProjectMgr.Site, HierarchyNode.SolutionExplorer); + if(uiHierarchy == null) + { + throw new InvalidOperationException(); + } + + ErrorHandler.ThrowOnFailure(uiHierarchy.ExpandItem(this.node.ProjectMgr, this.node.ID, EXPANDFLAGS.EXPF_ExpandFolder)); + + } + } + + /// + /// Opens the project item in the specified view. Not implemented because this abstract class dont know what to open + /// + /// Specifies the view kind in which to open the item + /// Window object + public virtual EnvDTE.Window Open(string ViewKind) + { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAProjectItems.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAProjectItems.cs new file mode 100644 index 0000000000..150f4237e7 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAProjectItems.cs @@ -0,0 +1,230 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using EnvDTE; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.VisualStudio.Project.Automation +{ + /// + /// Contains ProjectItem objects + /// + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] + [ComVisible(true), CLSCompliant(false)] + public class OAProjectItems : OANavigableProjectItems + { + #region ctor + public OAProjectItems(OAProject project, HierarchyNode nodeWithItems) + : base(project, nodeWithItems) + { + } + #endregion + + #region EnvDTE.ProjectItems + /// + /// Creates a new project item from an existing item template file and adds it to the project. + /// + /// The full path and file name of the template project file. + /// The file name to use for the new project item. + /// A ProjectItem object. + public override EnvDTE.ProjectItem AddFromTemplate(string fileName, string name) + { + + if(this.Project == null || this.Project.Project == null || this.Project.Project.Site == null || this.Project.Project.IsClosed) + { + throw new InvalidOperationException(); + } + + ProjectNode proj = this.Project.Project; + EnvDTE.ProjectItem itemAdded = null; + + using(AutomationScope scope = new AutomationScope(this.Project.Project.Site)) + { + // Determine the operation based on the extension of the filename. + // We should run the wizard only if the extension is vstemplate + // otherwise it's a clone operation + VSADDITEMOPERATION op; + + if(Utilities.IsTemplateFile(fileName)) + { + op = VSADDITEMOPERATION.VSADDITEMOP_RUNWIZARD; + } + else + { + op = VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE; + } + + VSADDRESULT[] result = new VSADDRESULT[1]; + + // It is not a very good idea to throw since the AddItem might return Cancel or Abort. + // The problem is that up in the call stack the wizard code does not check whether it has received a ProjectItem or not and will crash. + // The other problem is that we cannot get add wizard dialog back if a cancel or abort was returned because we throw and that code will never be executed. Typical catch 22. + ErrorHandler.ThrowOnFailure(proj.AddItem(this.NodeWithItems.ID, op, name, 0, new string[1] { fileName }, IntPtr.Zero, result)); + + string fileDirectory = proj.GetBaseDirectoryForAddingFiles(this.NodeWithItems); + string templateFilePath = System.IO.Path.Combine(fileDirectory, name); + itemAdded = this.EvaluateAddResult(result[0], templateFilePath); + } + + return itemAdded; + } + + /// + /// Adds a folder to the collection of ProjectItems with the given name. + /// + /// The kind must be null, empty string, or the string value of vsProjectItemKindPhysicalFolder. + /// Virtual folders are not supported by this implementation. + /// + /// The name of the new folder to add + /// A string representing a Guid of the folder kind. + /// A ProjectItem representing the newly added folder. + public override ProjectItem AddFolder(string name, string kind) + { + if(this.Project == null || this.Project.Project == null || this.Project.Project.Site == null || this.Project.Project.IsClosed) + { + throw new InvalidOperationException(); + } + //Verify name is not null or empty + Utilities.ValidateFileName(this.Project.Project.Site, name); + + //Verify that kind is null, empty, or a physical folder + if(!(string.IsNullOrEmpty(kind) || kind.Equals(EnvDTE.Constants.vsProjectItemKindPhysicalFolder))) + { + throw new ArgumentException("Parameter specification for AddFolder was not meet", "kind"); + } + + for(HierarchyNode child = this.NodeWithItems.FirstChild; child != null; child = child.NextSibling) + { + if(child.Caption.Equals(name, StringComparison.OrdinalIgnoreCase)) + { + throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, "Folder already exists with the name '{0}'", name)); + } + } + + ProjectNode proj = this.Project.Project; + + HierarchyNode newFolder = null; + using(AutomationScope scope = new AutomationScope(this.Project.Project.Site)) + { + + //In the case that we are adding a folder to a folder, we need to build up + //the path to the project node. + name = Path.Combine(this.NodeWithItems.VirtualNodeName, name); + + newFolder = proj.CreateFolderNodes(name); + } + + return newFolder.GetAutomationObject() as ProjectItem; + } + + /// + /// Copies a source file and adds it to the project. + /// + /// The path and file name of the project item to be added. + /// A ProjectItem object. + public override EnvDTE.ProjectItem AddFromFileCopy(string filePath) + { + return this.AddItem(filePath, VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE); + } + + /// + /// Adds a project item from a file that is installed in a project directory structure. + /// + /// The file name of the item to add as a project item. + /// A ProjectItem object. + public override EnvDTE.ProjectItem AddFromFile(string fileName) + { + // TODO: VSADDITEMOP_LINKTOFILE + return this.AddItem(fileName, VSADDITEMOPERATION.VSADDITEMOP_OPENFILE); + } + + #endregion + + #region helper methods + /// + /// Adds an item to the project. + /// + /// The full path of the item to add. + /// The to use when adding the item. + /// A ProjectItem object. + protected virtual EnvDTE.ProjectItem AddItem(string path, VSADDITEMOPERATION op) + { + if(this.Project == null || this.Project.Project == null || this.Project.Project.Site == null || this.Project.Project.IsClosed) + { + throw new InvalidOperationException(); + } + + ProjectNode proj = this.Project.Project; + + EnvDTE.ProjectItem itemAdded = null; + using(AutomationScope scope = new AutomationScope(this.Project.Project.Site)) + { + VSADDRESULT[] result = new VSADDRESULT[1]; + ErrorHandler.ThrowOnFailure(proj.AddItem(this.NodeWithItems.ID, op, path, 0, new string[1] { path }, IntPtr.Zero, result)); + + string fileName = System.IO.Path.GetFileName(path); + string fileDirectory = proj.GetBaseDirectoryForAddingFiles(this.NodeWithItems); + string filePathInProject = System.IO.Path.Combine(fileDirectory, fileName); + + itemAdded = this.EvaluateAddResult(result[0], filePathInProject); + } + + return itemAdded; + } + + /// + /// Evaluates the result of an add operation. + /// + /// The returned by the Add methods + /// The full path of the item added. + /// A ProjectItem object. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + protected virtual EnvDTE.ProjectItem EvaluateAddResult(VSADDRESULT result, string path) + { + if(result == VSADDRESULT.ADDRESULT_Success) + { + HierarchyNode nodeAdded = this.NodeWithItems.FindChild(path); + Debug.Assert(nodeAdded != null, "We should have been able to find the new element in the hierarchy"); + if(nodeAdded != null) + { + EnvDTE.ProjectItem item = null; + if(nodeAdded is FileNode) + { + item = new OAFileItem(this.Project, nodeAdded as FileNode); + } + else if(nodeAdded is NestedProjectNode) + { + item = new OANestedProjectItem(this.Project, nodeAdded as NestedProjectNode); + } + else + { + item = new OAProjectItem(this.Project, nodeAdded); + } + + this.Items.Add(item); + return item; + } + } + return null; + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAProperties.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAProperties.cs new file mode 100644 index 0000000000..433d08bcd5 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAProperties.cs @@ -0,0 +1,287 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace Microsoft.VisualStudio.Project.Automation +{ + /// + /// Contains all of the properties of a given object that are contained in a generic collection of properties. + /// + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] + [CLSCompliant(false), ComVisible(true)] + public class OAProperties : EnvDTE.Properties + { + #region fields + private NodeProperties target; + private Dictionary properties = new Dictionary(); + #endregion + + #region properties + /// + /// Defines the NodeProperties object that contains the defines the properties. + /// + public NodeProperties Target + { + get + { + return this.target; + } + } + + /// + /// The hierarchy node for the object which properties this item represent + /// + public HierarchyNode Node + { + get + { + return this.Target.Node; + } + } + + /// + /// Defines a dictionary of the properties contained. + /// + public Dictionary Properties + { + get + { + return this.properties; + } + } + #endregion + + #region ctor + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2214:DoNotCallOverridableMethodsInConstructors")] + public OAProperties(NodeProperties target) + { + System.Diagnostics.Debug.Assert(target != null); + + if (target == null) + { + throw new ArgumentNullException("target"); + } + + this.target = target; + this.AddPropertiesFromType(target.GetType()); + } + #endregion + + #region EnvDTE.Properties + /// + /// Microsoft Internal Use Only. + /// + public virtual object Application + { + get { return null; } + } + + /// + /// Gets a value indicating the number of objects in the collection. + /// + public int Count + { + get { return properties.Count; } + } + + /// + /// Gets the top-level extensibility object. + /// + public virtual EnvDTE.DTE DTE + { + get + { + if(this.target == null || this.target.Node == null || this.target.Node.ProjectMgr == null || this.target.Node.ProjectMgr.IsClosed || + this.target.Node.ProjectMgr.Site == null) + { + throw new InvalidOperationException(); + } + return this.target.Node.ProjectMgr.Site.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE; + } + } + + /// + /// Gets an enumeration for items in a collection. + /// + /// An enumerator. + public IEnumerator GetEnumerator() + { + if(this.properties == null) + { + yield return null; + } + + if(this.properties.Count == 0) + { + yield return new OANullProperty(this); + } + + IEnumerator enumerator = this.properties.Values.GetEnumerator(); + + while(enumerator.MoveNext()) + { + yield return enumerator.Current; + } + } + + /// + /// Returns an indexed member of a Properties collection. + /// + /// The index at which to return a mamber. + /// A Property object. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + public virtual EnvDTE.Property Item(object index) + { + if(index is string) + { + string indexAsString = (string)index; + if(this.properties.ContainsKey(indexAsString)) + { + return (EnvDTE.Property)this.properties[indexAsString]; + } + } + else if(index is int) + { + int realIndex = (int)index - 1; + if(realIndex >= 0 && realIndex < this.properties.Count) + { + IEnumerator enumerator = this.properties.Values.GetEnumerator(); + + int i = 0; + while(enumerator.MoveNext()) + { + if(i++ == realIndex) + { + return (EnvDTE.Property)enumerator.Current; + } + } + } + } + + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "index"); + } + /// + /// Gets the immediate parent object of a Properties collection. + /// + public virtual object Parent + { + get { return null; } + } + #endregion + + #region methods + /// + /// Add properties to the collection of properties filtering only those properties which are com-visible and AutomationBrowsable + /// + /// The type of NodeProperties the we should filter on + protected void AddPropertiesFromType(Type targetType) + { + Debug.Assert(targetType != null); + + if (targetType == null) + { + throw new ArgumentNullException("targetType"); + } + + // If the type is not COM visible, we do not expose any of the properties + if(!IsComVisible(targetType)) + return; + + // Add all properties being ComVisible and AutomationVisible + PropertyInfo[] propertyInfos = targetType.GetProperties(); + foreach(PropertyInfo propertyInfo in propertyInfos) + { + if(!IsInMap(propertyInfo) && IsComVisible(propertyInfo) && IsAutomationVisible(propertyInfo)) + { + AddProperty(propertyInfo); + } + } + } + #endregion + + #region virtual methods + /// + /// Creates a new OAProperty object and adds it to the current list of properties + /// + /// The property to be associated with an OAProperty object + protected virtual void AddProperty(PropertyInfo propertyInfo) + { + if (propertyInfo == null) + { + throw new ArgumentNullException("propertyInfo"); + } + + this.properties.Add(propertyInfo.Name, new OAProperty(this, propertyInfo)); + } + #endregion + + #region helper methods + + private bool IsInMap(PropertyInfo propertyInfo) + { + return this.properties.ContainsKey(propertyInfo.Name); + } + + private static bool IsAutomationVisible(PropertyInfo propertyInfo) + { + object[] customAttributesOnProperty = propertyInfo.GetCustomAttributes(typeof(AutomationBrowsableAttribute), true); + + foreach(AutomationBrowsableAttribute attr in customAttributesOnProperty) + { + if(!attr.Browsable) + { + return false; + } + } + return true; + } + + private static bool IsComVisible(Type targetType) + { + object[] customAttributesOnProperty = targetType.GetCustomAttributes(typeof(ComVisibleAttribute), true); + + foreach(ComVisibleAttribute attr in customAttributesOnProperty) + { + if(!attr.Value) + { + return false; + } + } + return true; + } + + private static bool IsComVisible(PropertyInfo propertyInfo) + { + object[] customAttributesOnProperty = propertyInfo.GetCustomAttributes(typeof(ComVisibleAttribute), true); + + foreach(ComVisibleAttribute attr in customAttributesOnProperty) + { + if(!attr.Value) + { + return false; + } + } + return true; + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAProperty.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAProperty.cs new file mode 100644 index 0000000000..7de119d203 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAProperty.cs @@ -0,0 +1,183 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Runtime.InteropServices; + +namespace Microsoft.VisualStudio.Project.Automation +{ + [CLSCompliant(false), ComVisible(true)] + public class OAProperty : EnvDTE.Property + { + #region fields + private OAProperties parent; + private PropertyInfo pi; + #endregion + + #region ctors + + public OAProperty(OAProperties parent, PropertyInfo pi) + { + this.parent = parent; + this.pi = pi; + } + #endregion + + #region EnvDTE.Property + /// + /// Microsoft Internal Use Only. + /// + public object Application + { + get { return null; } + } + + /// + /// Gets the Collection containing the Property object supporting this property. + /// + public EnvDTE.Properties Collection + { + get + { + //todo: EnvDTE.Property.Collection + return this.parent; + } + } + + /// + /// Gets the top-level extensibility object. + /// + public EnvDTE.DTE DTE + { + get + { + return this.parent.DTE; + } + } + + /// + /// Returns one element of a list. + /// + /// The index of the item to display. + /// The index of the item to display. Reserved for future use. + /// The index of the item to display. Reserved for future use. + /// The index of the item to display. Reserved for future use. + /// The value of a property + public object get_IndexedValue(object index1, object index2, object index3, object index4) + { + ParameterInfo[] par = pi.GetIndexParameters(); + int len = Math.Min(par.Length, 4); + if(len == 0) return this.Value; + object[] index = new object[len]; + Array.Copy(new object[4] { index1, index2, index3, index4 }, index, len); + return this.pi.GetValue(this.parent.Target, index); + } + + /// + /// Setter function to set properties values. + /// + /// + public void let_Value(object value) + { + this.Value = value; + } + + /// + /// Gets the name of the object. + /// + public string Name + { + get + { + return pi.Name; + } + } + + /// + /// Gets the number of indices required to access the value. + /// + public short NumIndices + { + get { return (short)pi.GetIndexParameters().Length; } + } + + /// + /// Sets or gets the object supporting the Property object. + /// + public object Object + { + get + { + return this.parent.Target; + } + set + { + } + } + + /// + /// Microsoft Internal Use Only. + /// + public EnvDTE.Properties Parent + { + get { return this.parent; } + } + + /// + /// Sets the value of the property at the specified index. + /// + /// The index of the item to set. + /// Reserved for future use. + /// Reserved for future use. + /// Reserved for future use. + /// The value to set. + public void set_IndexedValue(object index1, object index2, object index3, object index4, object value) + { + ParameterInfo[] par = pi.GetIndexParameters(); + int len = Math.Min(par.Length, 4); + if(len == 0) + { + this.Value = value; + } + else + { + object[] index = new object[len]; + Array.Copy(new object[4] { index1, index2, index3, index4 }, index, len); + + using(AutomationScope scope = new AutomationScope(this.parent.Target.Node.ProjectMgr.Site)) + { + this.pi.SetValue(this.parent.Target, value, index); + } + } + + } + + /// + /// Gets or sets the value of the property returned by the Property object. + /// + public object Value + { + get { return pi.GetValue(this.parent.Target, null); } + set + { + using(AutomationScope scope = new AutomationScope(this.parent.Target.Node.ProjectMgr.Site)) + { + this.pi.SetValue(this.parent.Target, value, null); + } + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAReferenceFolderItem.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAReferenceFolderItem.cs new file mode 100644 index 0000000000..4ffba74bc0 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAReferenceFolderItem.cs @@ -0,0 +1,70 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace Microsoft.VisualStudio.Project.Automation +{ + /// + /// Contains OAReferenceItem objects + /// + [SuppressMessage("Microsoft.Interoperability", "CA1405:ComVisibleTypeBaseTypesShouldBeComVisible")] + [ComVisible(true), CLSCompliant(false)] + public class OAReferenceFolderItem : OAProjectItem + { + #region ctors + public OAReferenceFolderItem(OAProject project, ReferenceContainerNode node) + : base(project, node) + { + } + + #endregion + + #region overridden methods + /// + /// Returns the project items collection of all the references defined for this project. + /// + public override EnvDTE.ProjectItems ProjectItems + { + get + { + return new OANavigableProjectItems(this.Project, this.GetListOfProjectItems(), this.Node); + } + } + + + #endregion + + #region Helper methods + private List GetListOfProjectItems() + { + List list = new List(); + for(HierarchyNode child = this.Node.FirstChild; child != null; child = child.NextSibling) + { + ReferenceNode node = child as ReferenceNode; + + if(node != null) + { + list.Add(new OAReferenceItem(this.Project, node)); + } + } + + return list; + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAReferenceItem.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAReferenceItem.cs new file mode 100644 index 0000000000..c83846e357 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OAReferenceItem.cs @@ -0,0 +1,95 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; + +namespace Microsoft.VisualStudio.Project.Automation +{ + /// + /// Represents the automation object equivalent to a ReferenceNode object + /// + [SuppressMessage("Microsoft.Interoperability", "CA1405:ComVisibleTypeBaseTypesShouldBeComVisible")] + [ComVisible(true), CLSCompliant(false)] + public class OAReferenceItem : OAProjectItem + { + #region ctors + public OAReferenceItem(OAProject project, ReferenceNode node) + : base(project, node) + { + } + + #endregion + + #region overridden methods + /// + /// Not implemented. If called throws invalid operation exception. + /// + public override void Delete() + { + throw new InvalidOperationException(); + } + + + /// + /// Not implemented. If called throws invalid operation exception. + /// + /// A Constants. vsViewKind indicating the type of view to use. + /// + public override EnvDTE.Window Open(string viewKind) + { + throw new InvalidOperationException(); + } + + /// + /// Gets or sets the name of the object. + /// + public override string Name + { + get + { + return base.Name; + } + set + { + throw new InvalidOperationException(); + } + } + + /// + /// Gets the ProjectItems collection containing the ProjectItem object supporting this property. + /// + public override EnvDTE.ProjectItems Collection + { + get + { + // Get the parent node (ReferenceContainerNode) + ReferenceContainerNode parentNode = this.Node.Parent as ReferenceContainerNode; + Debug.Assert(parentNode != null, "Failed to get the parent node"); + + // Get the ProjectItems object for the parent node + if(parentNode != null) + { + // The root node for the project + return ((OAReferenceFolderItem)parentNode.GetAutomationObject()).ProjectItems; + } + + return null; + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OASolutionFolder.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OASolutionFolder.cs new file mode 100644 index 0000000000..13304156c3 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/OASolutionFolder.cs @@ -0,0 +1,134 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.VisualStudio.Project.Automation +{ + [ComVisible(true), CLSCompliant(false)] + public class OASolutionFolder : EnvDTE80.SolutionFolder + where T : HierarchyNode + { + bool hidden = false; + T node; + + public OASolutionFolder(T associatedNode) + { + if(associatedNode == null) + { + throw new ArgumentNullException("associatedNode"); + } + + Debug.Assert(associatedNode.ProjectMgr is ProjectContainerNode, "Expecting obejct of type" + typeof(ProjectContainerNode).Name); + + if(!(associatedNode.ProjectMgr is ProjectContainerNode)) + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "associatedNode"); + + this.node = associatedNode; + } + + + #region SolutionFolder Members + + public virtual EnvDTE.Project AddFromFile(string fileName) + { + ProjectContainerNode projectContainer = (ProjectContainerNode)this.node.ProjectMgr; + ProjectElement newElement = new ProjectElement(projectContainer, fileName, ProjectFileConstants.SubProject); + NestedProjectNode newNode = projectContainer.AddExistingNestedProject(newElement, __VSCREATEPROJFLAGS.CPF_NOTINSLNEXPLR | __VSCREATEPROJFLAGS.CPF_SILENT | __VSCREATEPROJFLAGS.CPF_OPENFILE); + if(newNode == null) + return null; + // Now that the sub project was created, get its extensibility object so we can return it + object newProject = null; + if(ErrorHandler.Succeeded(newNode.NestedHierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ExtObject, out newProject))) + return newProject as EnvDTE.Project; + else + return null; + } + + public virtual EnvDTE.Project AddFromTemplate(string fileName, string destination, string projectName) + { + bool isVSTemplate = Utilities.IsTemplateFile(fileName); + + NestedProjectNode newNode = null; + if(isVSTemplate) + { + // Get the wizard to run, we will get called again and use the alternate code path + ProjectElement newElement = new ProjectElement(this.node.ProjectMgr, System.IO.Path.Combine(destination, projectName), ProjectFileConstants.SubProject); + newElement.SetMetadata(ProjectFileConstants.Template, fileName); + ((ProjectContainerNode)this.node.ProjectMgr).RunVsTemplateWizard(newElement, false); + } + else + { + if((String.IsNullOrEmpty(System.IO.Path.GetExtension(projectName)))) + { + string targetExtension = System.IO.Path.GetExtension(fileName); + projectName = System.IO.Path.ChangeExtension(projectName, targetExtension); + } + + ProjectContainerNode projectContainer = (ProjectContainerNode)this.node.ProjectMgr; + newNode = projectContainer.AddNestedProjectFromTemplate(fileName, destination, projectName, null, __VSCREATEPROJFLAGS.CPF_NOTINSLNEXPLR | __VSCREATEPROJFLAGS.CPF_SILENT | __VSCREATEPROJFLAGS.CPF_CLONEFILE); + } + if(newNode == null) + return null; + + // Now that the sub project was created, get its extensibility object so we can return it + object newProject = null; + if(ErrorHandler.Succeeded(newNode.NestedHierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ExtObject, out newProject))) + return newProject as EnvDTE.Project; + else + return null; + } + + public virtual EnvDTE.Project AddSolutionFolder(string Name) + { + throw new NotImplementedException(); + } + + public virtual EnvDTE.Project Parent + { + get + { + throw new NotImplementedException(); + } + } + + public virtual bool Hidden + { + get + { + return hidden; + } + set + { + hidden = value; + } + } + + public virtual EnvDTE.DTE DTE + { + get + { + return (EnvDTE.DTE)this.node.ProjectMgr.Site.GetService(typeof(EnvDTE.DTE)); + } + } + + #endregion + } + +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAAssemblyReference.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAAssemblyReference.cs new file mode 100644 index 0000000000..052e2cb43a --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAAssemblyReference.cs @@ -0,0 +1,177 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Diagnostics.CodeAnalysis; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using VSLangProj; + +namespace Microsoft.VisualStudio.Project.Automation +{ + [SuppressMessage("Microsoft.Interoperability", "CA1405:ComVisibleTypeBaseTypesShouldBeComVisible")] + [ComVisible(true)] + public class OAAssemblyReference : OAReferenceBase + { + public OAAssemblyReference(AssemblyReferenceNode assemblyReference) : + base(assemblyReference) + { + } + + #region Reference override + public override int BuildNumber + { + get + { + if((null == BaseReferenceNode.ResolvedAssembly) || + (null == BaseReferenceNode.ResolvedAssembly.Version)) + { + return 0; + } + return BaseReferenceNode.ResolvedAssembly.Version.Build; + } + } + public override string Culture + { + get + { + if((null == BaseReferenceNode.ResolvedAssembly) || + (null == BaseReferenceNode.ResolvedAssembly.CultureInfo)) + { + return string.Empty; + } + return BaseReferenceNode.ResolvedAssembly.CultureInfo.Name; + } + } + public override string Identity + { + get + { + // Note that in this function we use the assembly name instead of the resolved one + // because the identity of this reference is the assembly name needed by the project, + // not the specific instance found in this machine / environment. + if(null == BaseReferenceNode.AssemblyName) + { + return null; + } + return BaseReferenceNode.AssemblyName.FullName; + } + } + public override int MajorVersion + { + get + { + if((null == BaseReferenceNode.ResolvedAssembly) || + (null == BaseReferenceNode.ResolvedAssembly.Version)) + { + return 0; + } + return BaseReferenceNode.ResolvedAssembly.Version.Major; + } + } + public override int MinorVersion + { + get + { + if((null == BaseReferenceNode.ResolvedAssembly) || + (null == BaseReferenceNode.ResolvedAssembly.Version)) + { + return 0; + } + return BaseReferenceNode.ResolvedAssembly.Version.Minor; + } + } + + public override string PublicKeyToken + { + get + { + if((null == BaseReferenceNode.ResolvedAssembly) || + (null == BaseReferenceNode.ResolvedAssembly.GetPublicKeyToken())) + { + return null; + } + StringBuilder builder = new StringBuilder(); + byte[] publicKeyToken = BaseReferenceNode.ResolvedAssembly.GetPublicKeyToken(); + for(int i = 0; i < publicKeyToken.Length; i++) + { + // changed from MPFProj: + // http://mpfproj10.codeplex.com/WorkItem/View.aspx?WorkItemId=8257 + builder.AppendFormat("{0:x2}", publicKeyToken[i]); + } + return builder.ToString(); + } + } + + public override string Name + { + get + { + if(null != BaseReferenceNode.ResolvedAssembly) + { + return BaseReferenceNode.ResolvedAssembly.Name; + } + if(null != BaseReferenceNode.AssemblyName) + { + return BaseReferenceNode.AssemblyName.Name; + } + return null; + } + } + public override int RevisionNumber + { + get + { + if((null == BaseReferenceNode.ResolvedAssembly) || + (null == BaseReferenceNode.ResolvedAssembly.Version)) + { + return 0; + } + return BaseReferenceNode.ResolvedAssembly.Version.Revision; + } + } + public override bool StrongName + { + get + { + if((null == BaseReferenceNode.ResolvedAssembly) || + (0 == (BaseReferenceNode.ResolvedAssembly.Flags & AssemblyNameFlags.PublicKey))) + { + return false; + } + return true; + } + } + public override prjReferenceType Type + { + get + { + return prjReferenceType.prjReferenceTypeAssembly; + } + } + public override string Version + { + get + { + if((null == BaseReferenceNode.ResolvedAssembly) || + (null == BaseReferenceNode.ResolvedAssembly.Version)) + { + return string.Empty; + } + return BaseReferenceNode.ResolvedAssembly.Version.ToString(); + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OABuildManager.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OABuildManager.cs new file mode 100644 index 0000000000..9c3ded00e9 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OABuildManager.cs @@ -0,0 +1,109 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio.Shell.Interop; +using VSLangProj; +using Microsoft.VisualStudio; + +namespace Microsoft.VisualStudio.Project.Automation +{ + public class OABuildManager : ConnectionPointContainer, + IEventSource<_dispBuildManagerEvents>, + BuildManager, + BuildManagerEvents + { + private ProjectNode projectManager; + + public OABuildManager(ProjectNode project) + { + projectManager = project; + AddEventSource<_dispBuildManagerEvents>(this as IEventSource<_dispBuildManagerEvents>); + } + + + #region BuildManager Members + + public virtual string BuildDesignTimeOutput(string bstrOutputMoniker) + { + throw new NotImplementedException(); + } + + public virtual EnvDTE.Project ContainingProject + { + get { return projectManager.GetAutomationObject() as EnvDTE.Project; } + } + + public virtual EnvDTE.DTE DTE + { + get { return projectManager.Site.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE; } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] + public virtual object DesignTimeOutputMonikers + { + get { throw new NotImplementedException(); } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] + public virtual object Parent + { + get { throw new NotImplementedException(); } + } + + #endregion + + #region _dispBuildManagerEvents_Event Members + + public event _dispBuildManagerEvents_DesignTimeOutputDeletedEventHandler DesignTimeOutputDeleted; + + public event _dispBuildManagerEvents_DesignTimeOutputDirtyEventHandler DesignTimeOutputDirty; + + #endregion + + #region IEventSource<_dispBuildManagerEvents> Members + + void IEventSource<_dispBuildManagerEvents>.OnSinkAdded(_dispBuildManagerEvents sink) + { + DesignTimeOutputDeleted += new _dispBuildManagerEvents_DesignTimeOutputDeletedEventHandler(sink.DesignTimeOutputDeleted); + DesignTimeOutputDirty += new _dispBuildManagerEvents_DesignTimeOutputDirtyEventHandler(sink.DesignTimeOutputDirty); + } + + void IEventSource<_dispBuildManagerEvents>.OnSinkRemoved(_dispBuildManagerEvents sink) + { + DesignTimeOutputDeleted -= new _dispBuildManagerEvents_DesignTimeOutputDeletedEventHandler(sink.DesignTimeOutputDeleted); + DesignTimeOutputDirty -= new _dispBuildManagerEvents_DesignTimeOutputDirtyEventHandler(sink.DesignTimeOutputDirty); + } + + #endregion + + protected virtual void OnDesignTimeOutputDeleted(string outputMoniker) + { + var handlers = this.DesignTimeOutputDeleted; + if (handlers != null) + { + handlers(outputMoniker); + } + } + + protected virtual void OnDesignTimeOutputDirty(string outputMoniker) + { + var handlers = this.DesignTimeOutputDirty; + if (handlers != null) + { + handlers(outputMoniker); + } + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAComReference.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAComReference.cs new file mode 100644 index 0000000000..3e5f48bad3 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAComReference.cs @@ -0,0 +1,89 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.Runtime.InteropServices; + +namespace Microsoft.VisualStudio.Project.Automation +{ + [SuppressMessage("Microsoft.Interoperability", "CA1405:ComVisibleTypeBaseTypesShouldBeComVisible")] + [ComVisible(true)] + public class OAComReference : OAReferenceBase + { + public OAComReference(ComReferenceNode comReference) : + base(comReference) + { + } + + #region Reference override + public override string Culture + { + get + { + int locale = 0; + try + { + locale = int.Parse(BaseReferenceNode.LCID, CultureInfo.InvariantCulture); + } + catch(System.FormatException) + { + // Do Nothing + } + if(0 == locale) + { + return string.Empty; + } + CultureInfo culture = new CultureInfo(locale); + return culture.Name; + } + } + public override string Identity + { + get + { + return string.Format(CultureInfo.InvariantCulture, "{0}\\{1}", BaseReferenceNode.TypeGuid.ToString("B"), this.Version); + } + } + public override int MajorVersion + { + get { return BaseReferenceNode.MajorVersionNumber; } + } + public override int MinorVersion + { + get { return BaseReferenceNode.MinorVersionNumber; } + } + public override string Name + { + get { return BaseReferenceNode.Caption; } + } + public override VSLangProj.prjReferenceType Type + { + get + { + return VSLangProj.prjReferenceType.prjReferenceTypeActiveX; + } + } + public override string Version + { + get + { + Version version = new Version(BaseReferenceNode.MajorVersionNumber, BaseReferenceNode.MinorVersionNumber); + return version.ToString(); + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAProjectReference.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAProjectReference.cs new file mode 100644 index 0000000000..d304939aa0 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAProjectReference.cs @@ -0,0 +1,93 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using VSLangProj; + +namespace Microsoft.VisualStudio.Project.Automation +{ + /// + /// Represents a project reference of the solution + /// + [SuppressMessage("Microsoft.Interoperability", "CA1405:ComVisibleTypeBaseTypesShouldBeComVisible")] + [ComVisible(true)] + public class OAProjectReference : OAReferenceBase + { + public OAProjectReference(ProjectReferenceNode projectReference) : + base(projectReference) + { + } + + #region Reference override + public override string Culture + { + get { return string.Empty; } + } + public override string Name + { + get { return BaseReferenceNode.ReferencedProjectName; } + } + public override string Identity + { + get + { + return BaseReferenceNode.Caption; + } + } + public override string Path + { + get + { + return BaseReferenceNode.ReferencedProjectOutputPath; + } + } + public override EnvDTE.Project SourceProject + { + get + { + if(Guid.Empty == BaseReferenceNode.ReferencedProjectGuid) + { + return null; + } + IVsHierarchy hierarchy = VsShellUtilities.GetHierarchy(BaseReferenceNode.ProjectMgr.Site, BaseReferenceNode.ReferencedProjectGuid); + if(null == hierarchy) + { + return null; + } + object extObject; + if(Microsoft.VisualStudio.ErrorHandler.Succeeded( + hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ExtObject, out extObject))) + { + return extObject as EnvDTE.Project; + } + return null; + } + } + public override prjReferenceType Type + { + // TODO: Write the code that finds out the type of the output of the source project. + get { return prjReferenceType.prjReferenceTypeAssembly; } + } + public override string Version + { + get { return string.Empty; } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAReferenceBase.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAReferenceBase.cs new file mode 100644 index 0000000000..a00fbcb06a --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAReferenceBase.cs @@ -0,0 +1,183 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using VSLangProj; + +namespace Microsoft.VisualStudio.Project.Automation +{ + /// + /// Represents the automation equivalent of ReferenceNode + /// + /// + [SuppressMessage("Microsoft.Naming", "CA1715:IdentifiersShouldHaveCorrectPrefix", MessageId = "T")] + [ComVisible(true)] + public abstract class OAReferenceBase : Reference + where RefType : ReferenceNode + { + #region fields + private RefType referenceNode; + #endregion + + #region ctors + protected OAReferenceBase(RefType referenceNode) + { + this.referenceNode = referenceNode; + } + #endregion + + #region properties + protected RefType BaseReferenceNode + { + get { return referenceNode; } + } + #endregion + + #region Reference Members + public virtual int BuildNumber + { + get { return 0; } + } + + public virtual References Collection + { + get + { + return BaseReferenceNode.Parent.Object as References; + } + } + + public virtual EnvDTE.Project ContainingProject + { + get + { + return BaseReferenceNode.ProjectMgr.GetAutomationObject() as EnvDTE.Project; + } + } + + public virtual bool CopyLocal + { + get + { + throw new NotImplementedException(); + } + set + { + throw new NotImplementedException(); + } + } + + public virtual string Culture + { + get { throw new NotImplementedException(); } + } + + public virtual EnvDTE.DTE DTE + { + get + { + return BaseReferenceNode.ProjectMgr.Site.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE; + } + } + + public virtual string Description + { + get + { + return this.Name; + } + } + + public virtual string ExtenderCATID + { + get { throw new NotImplementedException(); } + } + + public virtual object ExtenderNames + { + get { throw new NotImplementedException(); } + } + + public virtual string Identity + { + get { throw new NotImplementedException(); } + } + + public virtual int MajorVersion + { + get { return 0; } + } + + public virtual int MinorVersion + { + get { return 0; } + } + + public virtual string Name + { + get { throw new NotImplementedException(); } + } + + public virtual string Path + { + get + { + return BaseReferenceNode.Url; + } + } + + public virtual string PublicKeyToken + { + get { throw new NotImplementedException(); } + } + + public virtual void Remove() + { + BaseReferenceNode.Remove(false); + } + + public virtual int RevisionNumber + { + get { return 0; } + } + + public virtual EnvDTE.Project SourceProject + { + get { return null; } + } + + public virtual bool StrongName + { + get { return false; } + } + + public virtual prjReferenceType Type + { + get { throw new NotImplementedException(); } + } + + public virtual string Version + { + get { return new Version().ToString(); } + } + + public virtual object get_Extender(string ExtenderName) + { + throw new NotImplementedException(); + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAReferences.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAReferences.cs new file mode 100644 index 0000000000..60be1c6e7b --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAReferences.cs @@ -0,0 +1,321 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.Shell.Interop; +using VSLangProj; +using ErrorHandler = Microsoft.VisualStudio.ErrorHandler; + +namespace Microsoft.VisualStudio.Project.Automation +{ + /// + /// Represents the automation object for the equivalent ReferenceContainerNode object + /// + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] + [ComVisible(true)] + public class OAReferences : ConnectionPointContainer, + IEventSource<_dispReferencesEvents>, + References, + ReferencesEvents + { + private ReferenceContainerNode container; + public OAReferences(ReferenceContainerNode containerNode) + { + container = containerNode; + AddEventSource<_dispReferencesEvents>(this as IEventSource<_dispReferencesEvents>); + container.OnChildAdded += new EventHandler(OnReferenceAdded); + container.OnChildRemoved += new EventHandler(OnReferenceRemoved); + } + + #region Private Members + private Reference AddFromSelectorData(VSCOMPONENTSELECTORDATA selector) + { + ReferenceNode refNode = container.AddReferenceFromSelectorData(selector); + if(null == refNode) + { + return null; + } + + return refNode.Object as Reference; + } + + private Reference FindByName(string stringIndex) + { + foreach(Reference refNode in this) + { + if(0 == string.Compare(refNode.Name, stringIndex, StringComparison.Ordinal)) + { + return refNode; + } + } + return null; + } + #endregion + + #region References Members + + public Reference Add(string bstrPath) + { + if(string.IsNullOrEmpty(bstrPath)) + { + return null; + } + VSCOMPONENTSELECTORDATA selector = new VSCOMPONENTSELECTORDATA(); + selector.type = VSCOMPONENTTYPE.VSCOMPONENTTYPE_File; + selector.bstrFile = bstrPath; + + return AddFromSelectorData(selector); + } + + public Reference AddActiveX(string bstrTypeLibGuid, int lMajorVer, int lMinorVer, int lLocaleId, string bstrWrapperTool) + { + VSCOMPONENTSELECTORDATA selector = new VSCOMPONENTSELECTORDATA(); + selector.type = VSCOMPONENTTYPE.VSCOMPONENTTYPE_Com2; + selector.guidTypeLibrary = new Guid(bstrTypeLibGuid); + selector.lcidTypeLibrary = (uint)lLocaleId; + selector.wTypeLibraryMajorVersion = (ushort)lMajorVer; + selector.wTypeLibraryMinorVersion = (ushort)lMinorVer; + + return AddFromSelectorData(selector); + } + + public Reference AddProject(EnvDTE.Project project) + { + if(null == project) + { + return null; + } + // Get the soulution. + IVsSolution solution = container.ProjectMgr.Site.GetService(typeof(SVsSolution)) as IVsSolution; + if(null == solution) + { + return null; + } + + // Get the hierarchy for this project. + IVsHierarchy projectHierarchy; + ErrorHandler.ThrowOnFailure(solution.GetProjectOfUniqueName(project.UniqueName, out projectHierarchy)); + + // Create the selector data. + VSCOMPONENTSELECTORDATA selector = new VSCOMPONENTSELECTORDATA(); + selector.type = VSCOMPONENTTYPE.VSCOMPONENTTYPE_Project; + + // Get the project reference string. + ErrorHandler.ThrowOnFailure(solution.GetProjrefOfProject(projectHierarchy, out selector.bstrProjRef)); + + selector.bstrTitle = project.Name; + selector.bstrFile = System.IO.Path.GetDirectoryName(project.FullName); + + return AddFromSelectorData(selector); + } + + public EnvDTE.Project ContainingProject + { + get + { + return container.ProjectMgr.GetAutomationObject() as EnvDTE.Project; + } + } + + public int Count + { + get + { + return container.EnumReferences().Count; + } + } + + public EnvDTE.DTE DTE + { + get + { + return container.ProjectMgr.Site.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE; + } + } + + public Reference Find(string bstrIdentity) + { + if(string.IsNullOrEmpty(bstrIdentity)) + { + return null; + } + foreach(Reference refNode in this) + { + if(null != refNode) + { + if(0 == string.Compare(bstrIdentity, refNode.Identity, StringComparison.Ordinal)) + { + return refNode; + } + } + } + return null; + } + + public IEnumerator GetEnumerator() + { + List references = new List(); + IEnumerator baseEnum = container.EnumReferences().GetEnumerator(); + if(null == baseEnum) + { + return references.GetEnumerator(); + } + while(baseEnum.MoveNext()) + { + ReferenceNode refNode = baseEnum.Current as ReferenceNode; + if(null == refNode) + { + continue; + } + Reference reference = refNode.Object as Reference; + if(null != reference) + { + references.Add(reference); + } + } + return references.GetEnumerator(); + } + + public Reference Item(object index) + { + string stringIndex = index as string; + if(null != stringIndex) + { + return FindByName(stringIndex); + } + // Note that this cast will throw if the index is not convertible to int. + int intIndex = (int)index; + IList refs = container.EnumReferences(); + if(null == refs) + { + throw new ArgumentOutOfRangeException("index"); + } + if((intIndex <= 0) || (intIndex > refs.Count)) + { + throw new ArgumentOutOfRangeException("index"); + } + // Let the implementation of IList<> throw in case of index not correct. + return refs[intIndex - 1].Object as Reference; + } + + public object Parent + { + get + { + return container.Parent.Object; + } + } + + #endregion + + #region _dispReferencesEvents_Event Members + public event _dispReferencesEvents_ReferenceAddedEventHandler ReferenceAdded; + public event _dispReferencesEvents_ReferenceChangedEventHandler ReferenceChanged; + public event _dispReferencesEvents_ReferenceRemovedEventHandler ReferenceRemoved; + #endregion + + #region Callbacks for the HierarchyNode events + private void OnReferenceAdded(object sender, HierarchyNodeEventArgs args) + { + // Validate the parameters. + if((container != sender as ReferenceContainerNode) || + (null == args) || (null == args.Child)) + { + return; + } + + // Check if there is any sink for this event. + if(null == ReferenceAdded) + { + return; + } + + // Check that the removed item implements the Reference interface. + Reference reference = args.Child.Object as Reference; + if(null != reference) + { + ReferenceAdded(reference); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode", + Justification="Support for this has not yet been added")] + private void OnReferenceChanged(object sender, HierarchyNodeEventArgs args) + { + // Validate the parameters. + if ((container != sender as ReferenceContainerNode) || + (null == args) || (null == args.Child)) + { + return; + } + + // Check if there is any sink for this event. + if (null == ReferenceChanged) + { + return; + } + + // Check that the removed item implements the Reference interface. + Reference reference = args.Child.Object as Reference; + if (null != reference) + { + ReferenceChanged(reference); + } + } + + private void OnReferenceRemoved(object sender, HierarchyNodeEventArgs args) + { + // Validate the parameters. + if((container != sender as ReferenceContainerNode) || + (null == args) || (null == args.Child)) + { + return; + } + + // Check if there is any sink for this event. + if(null == ReferenceRemoved) + { + return; + } + + // Check that the removed item implements the Reference interface. + Reference reference = args.Child.Object as Reference; + if(null != reference) + { + ReferenceRemoved(reference); + } + } + #endregion + + #region IEventSource<_dispReferencesEvents> Members + void IEventSource<_dispReferencesEvents>.OnSinkAdded(_dispReferencesEvents sink) + { + ReferenceAdded += new _dispReferencesEvents_ReferenceAddedEventHandler(sink.ReferenceAdded); + ReferenceChanged += new _dispReferencesEvents_ReferenceChangedEventHandler(sink.ReferenceChanged); + ReferenceRemoved += new _dispReferencesEvents_ReferenceRemovedEventHandler(sink.ReferenceRemoved); + } + + void IEventSource<_dispReferencesEvents>.OnSinkRemoved(_dispReferencesEvents sink) + { + ReferenceAdded -= new _dispReferencesEvents_ReferenceAddedEventHandler(sink.ReferenceAdded); + ReferenceChanged -= new _dispReferencesEvents_ReferenceChangedEventHandler(sink.ReferenceChanged); + ReferenceRemoved -= new _dispReferencesEvents_ReferenceRemovedEventHandler(sink.ReferenceRemoved); + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAVSProject.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAVSProject.cs new file mode 100644 index 0000000000..08e33c4ca8 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAVSProject.cs @@ -0,0 +1,217 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using EnvDTE; +using VSLangProj; + +namespace Microsoft.VisualStudio.Project.Automation +{ + /// + /// Represents an automation friendly version of a language-specific project. + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "OAVS")] + [ComVisible(true), CLSCompliant(false)] + public class OAVSProject : VSProject + { + #region fields + private ProjectNode project; + private OAVSProjectEvents events; + #endregion + + #region ctors + public OAVSProject(ProjectNode project) + { + this.project = project; + } + #endregion + + #region VSProject Members + + public virtual ProjectItem AddWebReference(string bstrUrl) + { + throw new NotImplementedException(); + } + + public virtual BuildManager BuildManager + { + get + { + return new OABuildManager(this.project); + } + } + + public virtual void CopyProject(string bstrDestFolder, string bstrDestUNCPath, prjCopyProjectOption copyProjectOption, string bstrUsername, string bstrPassword) + { + throw new NotImplementedException(); + } + + public virtual ProjectItem CreateWebReferencesFolder() + { + throw new NotImplementedException(); + } + + public virtual DTE DTE + { + get + { + return (EnvDTE.DTE)this.project.Site.GetService(typeof(EnvDTE.DTE)); + } + } + + public virtual VSProjectEvents Events + { + get + { + if(events == null) + events = new OAVSProjectEvents(this); + return events; + } + } + + public virtual void Exec(prjExecCommand command, int bSuppressUI, object varIn, out object pVarOut) + { + throw new NotImplementedException(); ; + } + + public virtual void GenerateKeyPairFiles(string strPublicPrivateFile, string strPublicOnlyFile) + { + throw new NotImplementedException(); ; + } + + public virtual string GetUniqueFilename(object pDispatch, string bstrRoot, string bstrDesiredExt) + { + throw new NotImplementedException(); ; + } + + public virtual Imports Imports + { + get + { + throw new NotImplementedException(); + } + } + + public virtual EnvDTE.Project Project + { + get + { + return this.project.GetAutomationObject() as EnvDTE.Project; + } + } + + public virtual References References + { + get + { + ReferenceContainerNode references = project.GetReferenceContainer() as ReferenceContainerNode; + if(null == references) + { + return null; + } + return references.Object as References; + } + } + + public virtual void Refresh() + { + throw new NotImplementedException(); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] + public virtual string TemplatePath + { + get + { + throw new NotImplementedException(); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] + public virtual ProjectItem WebReferencesFolder + { + get + { + throw new NotImplementedException(); + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] + public virtual bool WorkOffline + { + get + { + throw new NotImplementedException(); + } + set + { + throw new NotImplementedException(); + } + } + + #endregion + } + + /// + /// Provides access to language-specific project events + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "OAVS")] + [ComVisible(true), CLSCompliant(false)] + public class OAVSProjectEvents : VSProjectEvents + { + #region fields + private OAVSProject vsProject; + #endregion + + #region ctors + public OAVSProjectEvents(OAVSProject vsProject) + { + this.vsProject = vsProject; + } + #endregion + + #region VSProjectEvents Members + + public virtual BuildManagerEvents BuildManagerEvents + { + get + { + return vsProject.BuildManager as BuildManagerEvents; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations")] + public virtual ImportsEvents ImportsEvents + { + get + { + throw new NotImplementedException(); + } + } + + public virtual ReferencesEvents ReferencesEvents + { + get + { + return vsProject.References as ReferencesEvents; + } + } + + #endregion + } + +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAVSProjectItem.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAVSProjectItem.cs new file mode 100644 index 0000000000..f86dd38a24 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Automation/VSProject/OAVSProjectItem.cs @@ -0,0 +1,83 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using EnvDTE; +using VSLangProj; + +namespace Microsoft.VisualStudio.Project.Automation +{ + /// + /// Represents a language-specific project item + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "OAVS")] + [ComVisible(true), CLSCompliant(false)] + public class OAVSProjectItem : VSProjectItem + { + #region fields + private FileNode fileNode; + #endregion + + #region ctors + public OAVSProjectItem(FileNode fileNode) + { + this.FileNode = fileNode; + } + #endregion + + #region VSProjectItem Members + + public virtual EnvDTE.Project ContainingProject + { + get { return fileNode.ProjectMgr.GetAutomationObject() as EnvDTE.Project; } + } + + public virtual ProjectItem ProjectItem + { + get { return fileNode.GetAutomationObject() as ProjectItem; } + } + + public virtual DTE DTE + { + get { return (DTE)this.fileNode.ProjectMgr.Site.GetService(typeof(DTE)); } + } + + public virtual void RunCustomTool() + { + this.FileNode.RunGenerator(); + } + + #endregion + + #region public properties + /// + /// File Node property + /// + public FileNode FileNode + { + get + { + return fileNode; + } + set + { + fileNode = value; + } + } + #endregion + + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildDependency.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildDependency.cs new file mode 100644 index 0000000000..4999c4157e --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildDependency.cs @@ -0,0 +1,104 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.VisualStudio.Project +{ + public class BuildDependency : IVsBuildDependency + { + Guid referencedProjectGuid = Guid.Empty; + ProjectNode projectMgr = null; + + [CLSCompliant(false)] + public BuildDependency(ProjectNode projectMgr, Guid projectReference) + { + this.referencedProjectGuid = projectReference; + this.projectMgr = projectMgr; + } + + #region IVsBuildDependency methods + public int get_CanonicalName(out string canonicalName) + { + canonicalName = null; + return VSConstants.S_OK; + } + + public int get_Type(out System.Guid guidType) + { + // All our dependencies are build projects + guidType = VSConstants.GUID_VS_DEPTYPE_BUILD_PROJECT; + return VSConstants.S_OK; + } + + public int get_Description(out string description) + { + description = null; + return VSConstants.S_OK; + } + + [CLSCompliant(false)] + public int get_HelpContext(out uint helpContext) + { + helpContext = 0; + return VSConstants.E_NOTIMPL; + } + + public int get_HelpFile(out string helpFile) + { + helpFile = null; + return VSConstants.E_NOTIMPL; + } + + public int get_MustUpdateBefore(out int mustUpdateBefore) + { + // Must always update dependencies + mustUpdateBefore = 1; + + return VSConstants.S_OK; + } + + public int get_ReferredProject(out object unknownProject) + { + unknownProject = null; + + unknownProject = this.GetReferencedHierarchy(); + + // If we cannot find the referenced hierarchy return S_FALSE. + return (unknownProject == null) ? VSConstants.S_FALSE : VSConstants.S_OK; + } + + #endregion + + #region helper methods + private IVsHierarchy GetReferencedHierarchy() + { + IVsHierarchy hierarchy = null; + + if(this.referencedProjectGuid == Guid.Empty || this.projectMgr == null || this.projectMgr.IsClosed) + { + return hierarchy; + } + + return VsShellUtilities.GetHierarchy(this.projectMgr.Site, this.referencedProjectGuid); + + } + + #endregion + + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildItemGroupCollectionShim.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildItemGroupCollectionShim.cs new file mode 100644 index 0000000000..d2cab9aec3 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildItemGroupCollectionShim.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Build.BuildEngine; + +namespace Microsoft.VisualStudio.Project { + class BuildItemGroupCollectionShim : IEnumerable { + private Build.BuildEngine.BuildItemGroupCollection buildItemGroupCollection; + + public BuildItemGroupCollectionShim(Build.BuildEngine.BuildItemGroupCollection buildItemGroupCollection) { + // TODO: Complete member initialization + this.buildItemGroupCollection = buildItemGroupCollection; + } + + #region IEnumerable Members + + public IEnumerator GetEnumerator() { + foreach (var v in buildItemGroupCollection) { + yield return new BuildItemGroupShim((BuildItemGroup)v); + } + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + foreach (var v in buildItemGroupCollection) { + yield return new BuildItemGroupShim((BuildItemGroup)v); + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildItemGroupShim.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildItemGroupShim.cs new file mode 100644 index 0000000000..807a101f40 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildItemGroupShim.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.VisualStudio.Project { + class BuildItemGroupShim : IEnumerable { + private Build.BuildEngine.BuildItemGroup _group; + + public BuildItemGroupShim(Build.BuildEngine.BuildItemGroup group) { + _group = group; + } + + public bool IsImported { get { return _group.IsImported; } } + + #region IEnumerable Members + + public IEnumerator GetEnumerator() { + foreach (var x in _group) { + yield return new BuildItemShim((Build.BuildEngine.BuildItem)x); + } + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + foreach (var x in _group) { + yield return new BuildItemShim((Build.BuildEngine.BuildItem)x); + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildItemShim.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildItemShim.cs new file mode 100644 index 0000000000..215f622f02 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildItemShim.cs @@ -0,0 +1,17 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.VisualStudio.Project { + class BuildItemShim { + private Build.BuildEngine.BuildItem _buildItem; + + public BuildItemShim(Build.BuildEngine.BuildItem buildItem) { + _buildItem = buildItem; + } + public string FinalItemSpec { get { return _buildItem.FinalItemSpec; } } + + public string Name { get { return _buildItem.Name; } } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildPropertyGroupCollectionShim.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildPropertyGroupCollectionShim.cs new file mode 100644 index 0000000000..147cc4097f --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildPropertyGroupCollectionShim.cs @@ -0,0 +1,36 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Build.BuildEngine; + +namespace Microsoft.VisualStudio.Project { + class BuildPropertyGroupCollectionShim : IEnumerable { + private Build.BuildEngine.BuildPropertyGroupCollection buildPropertyGroupCollection; + + public BuildPropertyGroupCollectionShim(Build.BuildEngine.BuildPropertyGroupCollection buildPropertyGroupCollection) { + // TODO: Complete member initialization + this.buildPropertyGroupCollection = buildPropertyGroupCollection; + } + + #region IEnumerable Members + + public IEnumerator GetEnumerator() { + foreach (var v in buildPropertyGroupCollection) { + yield return new BuildPropertyGroupShim((BuildPropertyGroup)v); + } + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + foreach (var v in buildPropertyGroupCollection) { + yield return new BuildPropertyGroupShim((BuildPropertyGroup)v); + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildPropertyGroupShim.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildPropertyGroupShim.cs new file mode 100644 index 0000000000..e142491127 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildPropertyGroupShim.cs @@ -0,0 +1,39 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Build.BuildEngine; + +namespace Microsoft.VisualStudio.Project { + class BuildPropertyGroupShim : IEnumerable { + private readonly BuildPropertyGroup _buildPropertyGroup; + + public BuildPropertyGroupShim(BuildPropertyGroup buildPropertyGroup) { + _buildPropertyGroup = buildPropertyGroup; + } + + public bool IsImported { + get { + return _buildPropertyGroup.IsImported; + } + } + + #region IEnumerable Members + + public IEnumerator GetEnumerator() { + foreach (var property in _buildPropertyGroup) { + yield return new BuildPropertyShim((BuildProperty)property); + } + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildPropertyPage.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildPropertyPage.cs new file mode 100644 index 0000000000..11b383920b --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildPropertyPage.cs @@ -0,0 +1,88 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Enumerated list of the properties shown on the build property page + /// + internal enum BuildPropertyPageTag + { + OutputPath + } + + /// + /// Defines the properties on the build property page and the logic the binds the properties to project data (load and save) + /// + [CLSCompliant(false), ComVisible(true), Guid("9B3DEA40-7F29-4a17-87A4-00EE08E8241E")] + public class BuildPropertyPage : SettingsPage + { + #region fields + private string outputPath; + + public BuildPropertyPage() + { + this.Name = SR.GetString(SR.BuildCaption, CultureInfo.CurrentUICulture); + } + #endregion + + #region properties + [SRCategoryAttribute(SR.BuildCaption)] + [LocDisplayName(SR.OutputPath)] + [SRDescriptionAttribute(SR.OutputPathDescription)] + public string OutputPath + { + get { return this.outputPath; } + set { this.outputPath = value; this.IsDirty = true; } + } + #endregion + + #region overridden methods + public override string GetClassName() + { + return this.GetType().FullName; + } + + protected override void BindProperties() + { + if(this.ProjectMgr == null) + { + Debug.Assert(false); + return; + } + + this.outputPath = this.GetConfigProperty(BuildPropertyPageTag.OutputPath.ToString()); + } + + protected override int ApplyChanges() + { + if(this.ProjectMgr == null) + { + Debug.Assert(false); + return VSConstants.E_INVALIDARG; + } + + this.SetConfigProperty(BuildPropertyPageTag.OutputPath.ToString(), this.outputPath); + this.IsDirty = false; + return VSConstants.S_OK; + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildPropertyShim.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildPropertyShim.cs new file mode 100644 index 0000000000..4d1691a8a6 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/BuildPropertyShim.cs @@ -0,0 +1,16 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Build.BuildEngine; + +namespace Microsoft.VisualStudio.Project { + class BuildPropertyShim { + private readonly BuildProperty _property; + public BuildPropertyShim(BuildProperty property) { + _property = property; + } + + public string Name { get { return _property.Name; } } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ComReferenceNode.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ComReferenceNode.cs new file mode 100644 index 0000000000..ef68f070d4 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ComReferenceNode.cs @@ -0,0 +1,332 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.Win32; +using System.Collections.Generic; +using Microsoft.Build.Evaluation; +using Microsoft.Build.Execution; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// This type of node is used for references to COM components. + /// + [CLSCompliant(false)] + [ComVisible(true)] + public class ComReferenceNode : ReferenceNode + { + #region fields + private string typeName; + private Guid typeGuid; + private string projectRelativeFilePath; + private string installedFilePath; + private string minorVersionNumber; + private string majorVersionNumber; + private string lcid; + #endregion + + #region properties + public override string Caption + { + get { return this.typeName; } + } + + public override string Url + { + get + { + return this.projectRelativeFilePath; + } + } + + /// + /// Returns the Guid of the COM object. + /// + public Guid TypeGuid + { + get { return this.typeGuid; } + } + + /// + /// Returns the path where the COM object is installed. + /// + public string InstalledFilePath + { + get { return this.installedFilePath; } + } + + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "LCID")] + public string LCID + { + get { return lcid; } + } + + public int MajorVersionNumber + { + get + { + if(string.IsNullOrEmpty(majorVersionNumber)) + { + return 0; + } + return int.Parse(majorVersionNumber, CultureInfo.CurrentCulture); + } + } + public int MinorVersionNumber + { + get + { + if(string.IsNullOrEmpty(minorVersionNumber)) + { + return 0; + } + return int.Parse(minorVersionNumber, CultureInfo.CurrentCulture); + } + } + private Automation.OAComReference comReference; + internal override object Object + { + get + { + if(null == comReference) + { + comReference = new Automation.OAComReference(this); + } + return comReference; + } + } + #endregion + + #region ctors + /// + /// Constructor for the ComReferenceNode. + /// + public ComReferenceNode(ProjectNode root, ProjectElement element) + : base(root, element) + { + this.typeName = this.ItemNode.GetMetadata(ProjectFileConstants.Include); + string typeGuidAsString = this.ItemNode.GetMetadata(ProjectFileConstants.Guid); + if(typeGuidAsString != null) + { + this.typeGuid = new Guid(typeGuidAsString); + } + + this.majorVersionNumber = this.ItemNode.GetMetadata(ProjectFileConstants.VersionMajor); + this.minorVersionNumber = this.ItemNode.GetMetadata(ProjectFileConstants.VersionMinor); + this.lcid = this.ItemNode.GetMetadata(ProjectFileConstants.Lcid); + this.SetProjectItemsThatRelyOnReferencesToBeResolved(false); + this.SetInstalledFilePath(); + } + + /// + /// Overloaded constructor for creating a ComReferenceNode from selector data + /// + /// The Project node + /// The component selctor data. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes")] + public ComReferenceNode(ProjectNode root, VSCOMPONENTSELECTORDATA selectorData) + : base(root) + { + if(root == null) + { + throw new ArgumentNullException("root"); + } + if(selectorData.type == VSCOMPONENTTYPE.VSCOMPONENTTYPE_Project + || selectorData.type == VSCOMPONENTTYPE.VSCOMPONENTTYPE_ComPlus) + { + throw new ArgumentException("SelectorData cannot be of type VSCOMPONENTTYPE.VSCOMPONENTTYPE_Project or VSCOMPONENTTYPE.VSCOMPONENTTYPE_ComPlus", "selectorData"); + } + + // Initialize private state + this.typeName = selectorData.bstrTitle; + this.typeGuid = selectorData.guidTypeLibrary; + this.majorVersionNumber = selectorData.wTypeLibraryMajorVersion.ToString(CultureInfo.InvariantCulture); + this.minorVersionNumber = selectorData.wTypeLibraryMinorVersion.ToString(CultureInfo.InvariantCulture); + this.lcid = selectorData.lcidTypeLibrary.ToString(CultureInfo.InvariantCulture); + + // Check to see if the COM object actually exists. + this.SetInstalledFilePath(); + // If the value cannot be set throw. + if(String.IsNullOrEmpty(this.installedFilePath)) + { + throw new InvalidOperationException(); + } + } + #endregion + + #region methods + /// + /// Links a reference node to the project and hierarchy. + /// + protected override void BindReferenceData() + { + Debug.Assert(this.ItemNode != null, "The AssemblyName field has not been initialized"); + + // We need to create the project element at this point if it has not been created. + // We cannot do that from the ctor if input comes from a component selector data, since had we been doing that we would have added a project element to the project file. + // The problem with that approach is that we would need to remove the project element if the item cannot be added to the hierachy (E.g. It already exists). + // It is just safer to update the project file now. This is the intent of this method. + // Call MSBuild to build the target ResolveComReferences + if(this.ItemNode == null || this.ItemNode.Item == null) + { + this.ItemNode = this.GetProjectElementBasedOnInputFromComponentSelectorData(); + } + + this.SetProjectItemsThatRelyOnReferencesToBeResolved(true); + } + + /// + /// Checks if a reference is already added. The method parses all references and compares the the FinalItemSpec and the Guid. + /// + /// true if the assembly has already been added. + protected override bool IsAlreadyAdded() + { + ReferenceContainerNode referencesFolder = this.ProjectMgr.FindChild(ReferenceContainerNode.ReferencesNodeVirtualName) as ReferenceContainerNode; + Debug.Assert(referencesFolder != null, "Could not find the References node"); + + for(HierarchyNode n = referencesFolder.FirstChild; n != null; n = n.NextSibling) + { + ComReferenceNode refererenceNode = n as ComReferenceNode; + + if(refererenceNode != null) + { + // We check if the name and guids are the same + if(refererenceNode.TypeGuid == this.TypeGuid && String.Compare(refererenceNode.Caption, this.Caption, StringComparison.OrdinalIgnoreCase) == 0) + { + return true; + } + } + } + + return false; + } + + /// + /// Determines if this is node a valid node for painting the default reference icon. + /// + /// + protected override bool CanShowDefaultIcon() + { + return !String.IsNullOrEmpty(this.installedFilePath); + } + + /// + /// This is an helper method to convert the VSCOMPONENTSELECTORDATA recieved by the + /// implementer of IVsComponentUser into a ProjectElement that can be used to create + /// an instance of this class. + /// This should not be called for project reference or reference to managed assemblies. + /// + /// ProjectElement corresponding to the COM component passed in + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")] + private ProjectElement GetProjectElementBasedOnInputFromComponentSelectorData() + { + + ProjectElement element = new ProjectElement(this.ProjectMgr, this.typeName, ProjectFileConstants.COMReference); + + // Set the basic information regarding this COM component + element.SetMetadata(ProjectFileConstants.Guid, this.typeGuid.ToString("B")); + element.SetMetadata(ProjectFileConstants.VersionMajor, this.majorVersionNumber); + element.SetMetadata(ProjectFileConstants.VersionMinor, this.minorVersionNumber); + element.SetMetadata(ProjectFileConstants.Lcid, this.lcid); + element.SetMetadata(ProjectFileConstants.Isolated, false.ToString()); + + // See if a PIA exist for this component + TypeLibConverter typelib = new TypeLibConverter(); + string assemblyName; + string assemblyCodeBase; + if(typelib.GetPrimaryInteropAssembly(this.typeGuid, Int32.Parse(this.majorVersionNumber, CultureInfo.InvariantCulture), Int32.Parse(this.minorVersionNumber, CultureInfo.InvariantCulture), Int32.Parse(this.lcid, CultureInfo.InvariantCulture), out assemblyName, out assemblyCodeBase)) + { + element.SetMetadata(ProjectFileConstants.WrapperTool, WrapperToolAttributeValue.Primary.ToString().ToLowerInvariant()); + } + else + { + // MSBuild will have to generate an interop assembly + element.SetMetadata(ProjectFileConstants.WrapperTool, WrapperToolAttributeValue.TlbImp.ToString().ToLowerInvariant()); + element.SetMetadata(ProjectFileConstants.Private, true.ToString()); + } + return element; + } + + private void SetProjectItemsThatRelyOnReferencesToBeResolved(bool renameItemNode) + { + // Call MSBuild to build the target ResolveComReferences + bool success; + ErrorHandler.ThrowOnFailure(this.ProjectMgr.BuildTarget(MsBuildTarget.ResolveComReferences, out success)); + if(!success) + throw new InvalidOperationException(); + + // Now loop through the generated COM References to find the corresponding one + IEnumerable comReferences = this.ProjectMgr.BuildProject.GetItems(MsBuildGeneratedItemType.ComReferenceWrappers); + foreach (ProjectItem reference in comReferences) + { + if(String.Compare(reference.GetMetadataValue(ProjectFileConstants.Guid), this.typeGuid.ToString("B"), StringComparison.OrdinalIgnoreCase) == 0 + && String.Compare(reference.GetMetadataValue(ProjectFileConstants.VersionMajor), this.majorVersionNumber, StringComparison.OrdinalIgnoreCase) == 0 + && String.Compare(reference.GetMetadataValue(ProjectFileConstants.VersionMinor), this.minorVersionNumber, StringComparison.OrdinalIgnoreCase) == 0 + && String.Compare(reference.GetMetadataValue(ProjectFileConstants.Lcid), this.lcid, StringComparison.OrdinalIgnoreCase) == 0) + { + string name = reference.EvaluatedInclude; + if(Path.IsPathRooted(name)) + { + this.projectRelativeFilePath = name; + } + else + { + this.projectRelativeFilePath = Path.Combine(this.ProjectMgr.ProjectFolder, name); + } + + if(renameItemNode) + { + this.ItemNode.Rename(Path.GetFileNameWithoutExtension(name)); + } + break; + } + } + } + + /// + /// Verify that the TypeLib is registered and set the the installed file path of the com reference. + /// + /// + private void SetInstalledFilePath() + { + string registryPath = string.Format(CultureInfo.InvariantCulture, @"TYPELIB\{0:B}\{1:x}.{2:x}", this.typeGuid, this.MajorVersionNumber, this.MinorVersionNumber); + using(RegistryKey typeLib = Registry.ClassesRoot.OpenSubKey(registryPath)) + { + if(typeLib != null) + { + // Check if we need to set the name for this type. + if(string.IsNullOrEmpty(this.typeName)) + { + this.typeName = typeLib.GetValue(string.Empty) as string; + } + // Now get the path to the file that contains this type library. + using(RegistryKey installKey = typeLib.OpenSubKey(@"0\win32")) + { + this.installedFilePath = installKey.GetValue(String.Empty) as String; + } + } + } + } + + #endregion + } +} \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ConfigProvider.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ConfigProvider.cs new file mode 100644 index 0000000000..a4939644b8 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ConfigProvider.cs @@ -0,0 +1,756 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using MSBuild = Microsoft.Build.Evaluation; +using Microsoft.Build.Construction; + +/* This file provides a basefunctionallity for IVsCfgProvider2. + Instead of using the IVsProjectCfgEventsHelper object we have our own little sink and call our own helper methods + similiar to the interface. But there is no real benefit in inheriting from the interface in the first place. + Using the helper object seems to be: + a) undocumented + b) not really wise in the managed world +*/ +namespace Microsoft.VisualStudio.Project +{ + [CLSCompliant(false)] + [ComVisible(true)] + public class ConfigProvider : IVsCfgProvider2, IVsProjectCfgProvider, IVsExtensibleObject + { + #region fields + internal const string configString = " '$(Configuration)' == '{0}' "; + internal const string AnyCPUPlatform = "Any CPU"; + internal const string x86Platform = "x86"; + + private ProjectNode project; + private EventSinkCollection cfgEventSinks = new EventSinkCollection(); + private List, string>> newCfgProps = new List, string>>(); + private Dictionary configurationsList = new Dictionary(); + #endregion + + #region Properties + /// + /// The associated project. + /// + protected ProjectNode ProjectMgr + { + get + { + return this.project; + } + } + /// + /// If the project system wants to add custom properties to the property group then + /// they provide us with this data. + /// Returns/sets the [() ] collection + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1002:DoNotExposeGenericLists"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public virtual List, string>> NewConfigProperties + { + get + { + return newCfgProps; + } + set + { + newCfgProps = value; + } + } + + #endregion + + #region ctors + public ConfigProvider(ProjectNode manager) + { + this.project = manager; + } + #endregion + + #region methods + /// + /// Creates new Project Configuartion objects based on the configuration name. + /// + /// The name of the configuration + /// An instance of a ProjectConfig object. + protected ProjectConfig GetProjectConfiguration(string configName) + { + // if we already created it, return the cached one + if(configurationsList.ContainsKey(configName)) + { + return configurationsList[configName]; + } + + ProjectConfig requestedConfiguration = CreateProjectConfiguration(configName); + configurationsList.Add(configName, requestedConfiguration); + + return requestedConfiguration; + } + + protected virtual ProjectConfig CreateProjectConfiguration(string configName) + { + return new ProjectConfig(this.project, configName); + } + + #endregion + + #region IVsProjectCfgProvider methods + /// + /// Provides access to the IVsProjectCfg interface implemented on a project's configuration object. + /// + /// The canonical name of the configuration to access. + /// The IVsProjectCfg interface of the configuration identified by szProjectCfgCanonicalName. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int OpenProjectCfg(string projectCfgCanonicalName, out IVsProjectCfg projectCfg) + { + if(projectCfgCanonicalName == null) + { + throw new ArgumentNullException("projectCfgCanonicalName"); + } + + projectCfg = null; + + // Be robust in release + if(projectCfgCanonicalName == null) + { + return VSConstants.E_INVALIDARG; + } + + + Debug.Assert(this.project != null && this.project.BuildProject != null); + + string[] configs = GetPropertiesConditionedOn(ProjectFileConstants.Configuration); + + foreach(string config in configs) + { + if(String.Compare(config, projectCfgCanonicalName, StringComparison.OrdinalIgnoreCase) == 0) + { + projectCfg = this.GetProjectConfiguration(config); + if(projectCfg != null) + { + return VSConstants.S_OK; + } + else + { + return VSConstants.E_FAIL; + } + } + } + + return VSConstants.E_INVALIDARG; + } + + /// + /// Checks whether or not this configuration provider uses independent configurations. + /// + /// true if independent configurations are used, false if they are not used. By default returns true. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int get_UsesIndependentConfigurations(out int usesIndependentConfigurations) + { + usesIndependentConfigurations = 1; + return VSConstants.S_OK; + } + #endregion + + #region IVsCfgProvider2 methods + /// + /// Copies an existing configuration name or creates a new one. + /// + /// The name of the new configuration. + /// the name of the configuration to copy, or a null reference, indicating that AddCfgsOfCfgName should create a new configuration. + /// Flag indicating whether or not the new configuration is private. If fPrivate is set to true, the configuration is private. If set to false, the configuration is public. This flag can be ignored. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int AddCfgsOfCfgName(string name, string cloneName, int fPrivate) + { + // We need to QE/QS the project file + if(!this.ProjectMgr.QueryEditProjectFile(false)) + { + throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + + // First create the condition that represent the configuration we want to clone + string condition = (cloneName == null ? String.Empty : String.Format(CultureInfo.InvariantCulture, configString, cloneName).Trim()); + + // Get all configs + List configGroup = new List(this.project.BuildProject.Xml.PropertyGroups); + ProjectPropertyGroupElement configToClone = null; + + if(cloneName != null) + { + // Find the configuration to clone + foreach (ProjectPropertyGroupElement currentConfig in configGroup) + { + // Only care about conditional property groups + if(currentConfig.Condition == null || currentConfig.Condition.Length == 0) + continue; + + // Skip if it isn't the group we want + if(String.Compare(currentConfig.Condition.Trim(), condition, StringComparison.OrdinalIgnoreCase) != 0) + continue; + + configToClone = currentConfig; + } + } + + ProjectPropertyGroupElement newConfig = null; + if(configToClone != null) + { + // Clone the configuration settings + newConfig = this.project.ClonePropertyGroup(configToClone); + //Will be added later with the new values to the path + + foreach (ProjectPropertyElement property in newConfig.Properties) + { + if (property.Name.Equals("OutputPath", StringComparison.OrdinalIgnoreCase)) + { + property.Parent.RemoveChild(property); + } + } + } + else + { + // no source to clone from, lets just create a new empty config + newConfig = this.project.BuildProject.Xml.AddPropertyGroup(); + // Get the list of property name, condition value from the config provider + IList, string>> propVals = this.NewConfigProperties; + foreach(KeyValuePair, string> data in propVals) + { + KeyValuePair propData = data.Key; + string value = data.Value; + ProjectPropertyElement newProperty = newConfig.AddProperty(propData.Key, value); + if(!String.IsNullOrEmpty(propData.Value)) + newProperty.Condition = propData.Value; + } + } + + + //add the output path + string outputBasePath = this.ProjectMgr.OutputBaseRelativePath; + if(outputBasePath.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) + outputBasePath = Path.GetDirectoryName(outputBasePath); + newConfig.AddProperty("OutputPath", Path.Combine(outputBasePath, name) + Path.DirectorySeparatorChar.ToString()); + + // Set the condition that will define the new configuration + string newCondition = String.Format(CultureInfo.InvariantCulture, configString, name); + newConfig.Condition = newCondition; + + NotifyOnCfgNameAdded(name); + return VSConstants.S_OK; + } + + /// + /// Copies an existing platform name or creates a new one. + /// + /// The name of the new platform. + /// The name of the platform to copy, or a null reference, indicating that AddCfgsOfPlatformName should create a new platform. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int AddCfgsOfPlatformName(string platformName, string clonePlatformName) + { + return VSConstants.E_NOTIMPL; + } + + /// + /// Deletes a specified configuration name. + /// + /// The name of the configuration to be deleted. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int DeleteCfgsOfCfgName(string name) + { + // We need to QE/QS the project file + if(!this.ProjectMgr.QueryEditProjectFile(false)) + { + throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + + if(name == null) + { + Debug.Fail(String.Format(CultureInfo.CurrentCulture, "Name of the configuration should not be null if you want to delete it from project: {0}", this.project.BuildProject.FullPath)); + // The configuration " '$(Configuration)' == " does not exist, so technically the goal + // is achieved so return S_OK + return VSConstants.S_OK; + } + // Verify that this config exist + string[] configs = GetPropertiesConditionedOn(ProjectFileConstants.Configuration); + foreach(string config in configs) + { + if(String.Compare(config, name, StringComparison.OrdinalIgnoreCase) == 0) + { + // Create condition of config to remove + string condition = String.Format(CultureInfo.InvariantCulture, configString, config); + + foreach (ProjectPropertyGroupElement element in this.project.BuildProject.Xml.PropertyGroups) + { + if(String.Equals(element.Condition, condition, StringComparison.OrdinalIgnoreCase)) + { + element.Parent.RemoveChild(element); + } + } + + NotifyOnCfgNameDeleted(name); + } + } + + return VSConstants.S_OK; + } + + /// + /// Deletes a specified platform name. + /// + /// The platform name to delet. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int DeleteCfgsOfPlatformName(string platName) + { + return VSConstants.E_NOTIMPL; + } + + /// + /// Returns the existing configurations stored in the project file. + /// + /// Specifies the requested number of property names. If this number is unknown, celt can be zero. + /// On input, an allocated array to hold the number of configuration property names specified by celt. This parameter can also be a null reference if the celt parameter is zero. + /// On output, names contains configuration property names. + /// The actual number of property names returned. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int GetCfgNames(uint celt, string[] names, uint[] actual) + { + // get's called twice, once for allocation, then for retrieval + int i = 0; + + string[] configList = GetPropertiesConditionedOn(ProjectFileConstants.Configuration); + + if(names != null) + { + foreach(string config in configList) + { + names[i++] = config; + if(i == celt) + break; + } + } + else + i = configList.Length; + + if(actual != null) + { + actual[0] = (uint)i; + } + + return VSConstants.S_OK; + } + + /// + /// Returns the configuration associated with a specified configuration or platform name. + /// + /// The name of the configuration to be returned. + /// The name of the platform for the configuration to be returned. + /// The implementation of the IVsCfg interface. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int GetCfgOfName(string name, string platName, out IVsCfg cfg) + { + cfg = null; + cfg = this.GetProjectConfiguration(name); + + return VSConstants.S_OK; + } + + /// + /// Returns a specified configuration property. + /// + /// Specifies the property identifier for the property to return. For valid propid values, see __VSCFGPROPID. + /// The value of the property. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int GetCfgProviderProperty(int propid, out object var) + { + var = false; + switch((__VSCFGPROPID)propid) + { + case __VSCFGPROPID.VSCFGPROPID_SupportsCfgAdd: + var = true; + break; + + case __VSCFGPROPID.VSCFGPROPID_SupportsCfgDelete: + var = true; + break; + + case __VSCFGPROPID.VSCFGPROPID_SupportsCfgRename: + var = true; + break; + + case __VSCFGPROPID.VSCFGPROPID_SupportsPlatformAdd: + var = false; + break; + + case __VSCFGPROPID.VSCFGPROPID_SupportsPlatformDelete: + var = false; + break; + } + return VSConstants.S_OK; + } + + /// + /// Returns the per-configuration objects for this object. + /// + /// Number of configuration objects to be returned or zero, indicating a request for an unknown number of objects. + /// On input, pointer to an interface array or a null reference. On output, this parameter points to an array of IVsCfg interfaces belonging to the requested configuration objects. + /// The number of configuration objects actually returned or a null reference, if this information is not necessary. + /// Flags that specify settings for project configurations, or a null reference (Nothing in Visual Basic) if no additional flag settings are required. For valid prgrFlags values, see __VSCFGFLAGS. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int GetCfgs(uint celt, IVsCfg[] a, uint[] actual, uint[] flags) + { + if(flags != null) + flags[0] = 0; + + int i = 0; + string[] configList = GetPropertiesConditionedOn(ProjectFileConstants.Configuration); + + if(a != null) + { + foreach(string configName in configList) + { + a[i] = this.GetProjectConfiguration(configName); + + i++; + if(i == celt) + break; + } + } + else + i = configList.Length; + + if(actual != null) + actual[0] = (uint)i; + + return VSConstants.S_OK; + } + + /// + /// Returns one or more platform names. + /// + /// Specifies the requested number of platform names. If this number is unknown, celt can be zero. + /// On input, an allocated array to hold the number of platform names specified by celt. This parameter can also be a null reference if the celt parameter is zero. On output, names contains platform names. + /// The actual number of platform names returned. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int GetPlatformNames(uint celt, string[] names, uint[] actual) + { + string[] platforms = this.GetPlatformsFromProject(); + return GetPlatforms(celt, names, actual, platforms); + } + + /// + /// Returns the set of platforms that are installed on the user's machine. + /// + /// Specifies the requested number of supported platform names. If this number is unknown, celt can be zero. + /// On input, an allocated array to hold the number of names specified by celt. This parameter can also be a null reference (Nothing in Visual Basic)if the celt parameter is zero. On output, names contains the names of supported platforms + /// The actual number of platform names returned. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int GetSupportedPlatformNames(uint celt, string[] names, uint[] actual) + { + string[] platforms = this.GetSupportedPlatformsFromProject(); + return GetPlatforms(celt, names, actual, platforms); + } + + /// + /// Assigns a new name to a configuration. + /// + /// The old name of the target configuration. + /// The new name of the target configuration. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int RenameCfgsOfCfgName(string old, string newname) + { + // First create the condition that represent the configuration we want to rename + string condition = String.Format(CultureInfo.InvariantCulture, configString, old).Trim(); + + foreach (ProjectPropertyGroupElement config in this.project.BuildProject.Xml.PropertyGroups) + { + // Only care about conditional property groups + if(config.Condition == null || config.Condition.Length == 0) + continue; + + // Skip if it isn't the group we want + if(String.Compare(config.Condition.Trim(), condition, StringComparison.OrdinalIgnoreCase) != 0) + continue; + + // Change the name + config.Condition = String.Format(CultureInfo.InvariantCulture, configString, newname); + // Update the name in our config list + if(configurationsList.ContainsKey(old)) + { + ProjectConfig configuration = configurationsList[old]; + configurationsList.Remove(old); + configurationsList.Add(newname, configuration); + // notify the configuration of its new name + configuration.ConfigName = newname; + } + + NotifyOnCfgNameRenamed(old, newname); + } + + return VSConstants.S_OK; + } + + /// + /// Cancels a registration for configuration event notification. + /// + /// The cookie used for registration. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int UnadviseCfgProviderEvents(uint cookie) + { + this.cfgEventSinks.RemoveAt(cookie); + return VSConstants.S_OK; + } + + /// + /// Registers the caller for configuration event notification. + /// + /// Reference to the IVsCfgProviderEvents interface to be called to provide notification of configuration events. + /// Reference to a token representing the completed registration + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int AdviseCfgProviderEvents(IVsCfgProviderEvents sink, out uint cookie) + { + cookie = this.cfgEventSinks.Add(sink); + return VSConstants.S_OK; + } + #endregion + + #region IVsExtensibleObject Members + + /// + /// Proved access to an IDispatchable object being a list of configuration properties + /// + /// Combined Name and Platform for the configuration requested + /// The IDispatchcable object + /// S_OK if successful + public virtual int GetAutomationObject(string configurationName, out object configurationProperties) + { + //Init out param + configurationProperties = null; + + string name, platform; + if(!ProjectConfig.TrySplitConfigurationCanonicalName(configurationName, out name, out platform)) + { + return VSConstants.E_INVALIDARG; + } + + // Get the configuration + IVsCfg cfg; + ErrorHandler.ThrowOnFailure(this.GetCfgOfName(name, platform, out cfg)); + + // Get the properties of the configuration + configurationProperties = ((ProjectConfig)cfg).ConfigurationProperties; + + return VSConstants.S_OK; + + } + #endregion + + #region helper methods + /// + /// Called when a new configuration name was added. + /// + /// The name of configuration just added. + private void NotifyOnCfgNameAdded(string name) + { + foreach(IVsCfgProviderEvents sink in this.cfgEventSinks) + { + ErrorHandler.ThrowOnFailure(sink.OnCfgNameAdded(name)); + } + } + + /// + /// Called when a config name was deleted. + /// + /// The name of the configuration. + private void NotifyOnCfgNameDeleted(string name) + { + foreach(IVsCfgProviderEvents sink in this.cfgEventSinks) + { + ErrorHandler.ThrowOnFailure(sink.OnCfgNameDeleted(name)); + } + } + + /// + /// Called when a config name was renamed + /// + /// Old configuration name + /// New configuration name + private void NotifyOnCfgNameRenamed(string oldName, string newName) + { + foreach(IVsCfgProviderEvents sink in this.cfgEventSinks) + { + ErrorHandler.ThrowOnFailure(sink.OnCfgNameRenamed(oldName, newName)); + } + } + + /// + /// Called when a platform name was added + /// + /// The name of the platform. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + private void NotifyOnPlatformNameAdded(string platformName) + { + foreach(IVsCfgProviderEvents sink in this.cfgEventSinks) + { + ErrorHandler.ThrowOnFailure(sink.OnPlatformNameAdded(platformName)); + } + } + + /// + /// Called when a platform name was deleted + /// + /// The name of the platform. + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + private void NotifyOnPlatformNameDeleted(string platformName) + { + foreach(IVsCfgProviderEvents sink in this.cfgEventSinks) + { + ErrorHandler.ThrowOnFailure(sink.OnPlatformNameDeleted(platformName)); + } + } + + /// + /// Gets all the platforms defined in the project + /// + /// An array of platform names. + private string[] GetPlatformsFromProject() + { + string[] platforms = GetPropertiesConditionedOn(ProjectFileConstants.Platform); + + if(platforms == null || platforms.Length == 0) + { + return new string[] { x86Platform, AnyCPUPlatform }; + } + + for(int i = 0; i < platforms.Length; i++) + { + platforms[i] = ConvertPlatformToVsProject(platforms[i]); + } + + return platforms; + } + + /// + /// Return the supported platform names. + /// + /// An array of supported platform names. + private string[] GetSupportedPlatformsFromProject() + { + string platforms = this.ProjectMgr.BuildProject.GetPropertyValue(ProjectFileConstants.AvailablePlatforms); + + if(platforms == null) + { + return new string[] { }; + } + + if(platforms.Contains(",")) + { + return platforms.Split(','); + } + + return new string[] { platforms }; + } + + /// + /// Helper function to convert AnyCPU to Any CPU. + /// + /// The oldname. + /// The new name. + private static string ConvertPlatformToVsProject(string oldPlatformName) + { + if(String.Compare(oldPlatformName, ProjectFileValues.AnyCPU, StringComparison.OrdinalIgnoreCase) == 0) + { + return AnyCPUPlatform; + } + + return oldPlatformName; + } + + /// + /// Common method for handling platform names. + /// + /// Specifies the requested number of platform names. If this number is unknown, celt can be zero. + /// On input, an allocated array to hold the number of platform names specified by celt. This parameter can also be null if the celt parameter is zero. On output, names contains platform names + /// A count of the actual number of platform names returned. + /// An array of available platform names + /// A count of the actual number of platform names returned. + /// The platforms array is never null. It is assured by the callers. + private static int GetPlatforms(uint celt, string[] names, uint[] actual, string[] platforms) + { + Debug.Assert(platforms != null, "The plaforms array should never be null"); + if(names == null) + { + if(actual == null || actual.Length == 0) + { + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "actual"); + } + + actual[0] = (uint)platforms.Length; + return VSConstants.S_OK; + } + + //Degenarate case + if(celt == 0) + { + if(actual != null && actual.Length != 0) + { + actual[0] = (uint)platforms.Length; + } + + return VSConstants.S_OK; + } + + uint returned = 0; + for(int i = 0; i < platforms.Length && names.Length > returned; i++) + { + names[returned] = platforms[i]; + returned++; + } + + if(actual != null && actual.Length != 0) + { + actual[0] = returned; + } + + if(celt > returned) + { + return VSConstants.S_FALSE; + } + + return VSConstants.S_OK; + } + #endregion + + /// + /// Get all the configurations in the project. + /// + private string[] GetPropertiesConditionedOn(string constant) + { + List configurations = null; + this.project.BuildProject.ReevaluateIfNecessary(); + this.project.BuildProject.ConditionedProperties.TryGetValue(constant, out configurations); + + return (configurations == null) ? new string[] { } : configurations.ToArray(); + } + + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ConfigurationProperties.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ConfigurationProperties.cs new file mode 100644 index 0000000000..ddf03715f7 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ConfigurationProperties.cs @@ -0,0 +1,64 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Defines the config dependent properties exposed through automation + /// + [ComVisible(true)] + [Guid("21f73a8f-91d7-4085-9d4f-c48ee235ee5b")] + public interface IProjectConfigProperties + { + string OutputPath { get; set; } + } + + /// + /// Implements the configuration dependent properties interface + /// + [CLSCompliant(false), ComVisible(true)] + [ClassInterface(ClassInterfaceType.None)] + public class ProjectConfigProperties : IProjectConfigProperties + { + #region fields + private ProjectConfig projectConfig; + #endregion + + #region ctors + public ProjectConfigProperties(ProjectConfig projectConfig) + { + this.projectConfig = projectConfig; + } + #endregion + + #region IProjectConfigProperties Members + + public virtual string OutputPath + { + get + { + return this.projectConfig.GetConfigurationProperty(BuildPropertyPageTag.OutputPath.ToString(), true); + } + set + { + this.projectConfig.SetConfigurationProperty(BuildPropertyPageTag.OutputPath.ToString(), value); + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/DataObject.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/DataObject.cs new file mode 100644 index 0000000000..ce1212c573 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/DataObject.cs @@ -0,0 +1,596 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Security.Permissions; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; + +namespace Microsoft.VisualStudio.Project +{ + internal enum tagDVASPECT + { + DVASPECT_CONTENT = 1, + DVASPECT_THUMBNAIL = 2, + DVASPECT_ICON = 4, + DVASPECT_DOCPRINT = 8 + } + + internal enum tagTYMED + { + TYMED_HGLOBAL = 1, + TYMED_FILE = 2, + TYMED_ISTREAM = 4, + TYMED_ISTORAGE = 8, + TYMED_GDI = 16, + TYMED_MFPICT = 32, + TYMED_ENHMF = 64, + TYMED_NULL = 0 + } + + internal sealed class DataCacheEntry : IDisposable + { + #region fields + /// + /// Defines an object that will be a mutex for this object for synchronizing thread calls. + /// + private static volatile object Mutex = new object(); + + private FORMATETC format; + + private long data; + + private DATADIR dataDir; + + private bool isDisposed; + #endregion + + #region properties + internal FORMATETC Format + { + get + { + return this.format; + } + } + + internal long Data + { + get + { + return this.data; + } + } + + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal DATADIR DataDir + { + get + { + return this.dataDir; + } + } + + #endregion + + /// + /// The IntPtr is data allocated that should be removed. It is allocated by the ProcessSelectionData method. + /// + internal DataCacheEntry(FORMATETC fmt, IntPtr data, DATADIR dir) + { + this.format = fmt; + this.data = (long)data; + this.dataDir = dir; + } + + #region Dispose + ~DataCacheEntry() + { + Dispose(false); + } + + /// + /// The IDispose interface Dispose method for disposing the object determinastically. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + /// + /// The method that does the cleanup. + /// + /// + private void Dispose(bool disposing) + { + // Everybody can go here. + if(!this.isDisposed) + { + // Synchronize calls to the Dispose simulteniously. + lock(Mutex) + { + if(disposing && this.data != 0) + { + Marshal.FreeHGlobal((IntPtr)this.data); + this.data = 0; + } + + this.isDisposed = true; + } + } + } + #endregion + } + + /// + /// Unfortunately System.Windows.Forms.IDataObject and + /// Microsoft.VisualStudio.OLE.Interop.IDataObject are different... + /// + internal sealed class DataObject : IDataObject + { + #region fields + internal const int DATA_S_SAMEFORMATETC = 0x00040130; + EventSinkCollection map; + ArrayList entries; + #endregion + + internal DataObject() + { + this.map = new EventSinkCollection(); + this.entries = new ArrayList(); + } + + internal void SetData(FORMATETC format, IntPtr data) + { + this.entries.Add(new DataCacheEntry(format, data, DATADIR.DATADIR_SET)); + } + + #region IDataObject methods + int IDataObject.DAdvise(FORMATETC[] e, uint adv, IAdviseSink sink, out uint cookie) + { + if (e == null) + { + throw new ArgumentNullException("e"); + } + + STATDATA sdata = new STATDATA(); + + sdata.ADVF = adv; + sdata.FORMATETC = e[0]; + sdata.pAdvSink = sink; + cookie = this.map.Add(sdata); + sdata.dwConnection = cookie; + return 0; + } + + void IDataObject.DUnadvise(uint cookie) + { + this.map.RemoveAt(cookie); + } + + int IDataObject.EnumDAdvise(out IEnumSTATDATA e) + { + e = new EnumSTATDATA((IEnumerable)this.map); + return 0; //?? + } + + int IDataObject.EnumFormatEtc(uint direction, out IEnumFORMATETC penum) + { + penum = new EnumFORMATETC((DATADIR)direction, (IEnumerable)this.entries); + return 0; + } + + int IDataObject.GetCanonicalFormatEtc(FORMATETC[] format, FORMATETC[] fmt) + { + throw new System.Runtime.InteropServices.COMException("", DATA_S_SAMEFORMATETC); + } + + void IDataObject.GetData(FORMATETC[] fmt, STGMEDIUM[] m) + { + STGMEDIUM retMedium = new STGMEDIUM(); + + if(fmt == null || fmt.Length < 1) + return; + + foreach(DataCacheEntry e in this.entries) + { + if(e.Format.cfFormat == fmt[0].cfFormat /*|| fmt[0].cfFormat == InternalNativeMethods.CF_HDROP*/) + { + retMedium.tymed = e.Format.tymed; + + // Caller must delete the memory. + retMedium.unionmember = DragDropHelper.CopyHGlobal(new IntPtr(e.Data)); + break; + } + } + + if(m != null && m.Length > 0) + m[0] = retMedium; + } + + void IDataObject.GetDataHere(FORMATETC[] fmt, STGMEDIUM[] m) + { + } + + int IDataObject.QueryGetData(FORMATETC[] fmt) + { + if(fmt == null || fmt.Length < 1) + return VSConstants.S_FALSE; + + foreach(DataCacheEntry e in this.entries) + { + if(e.Format.cfFormat == fmt[0].cfFormat /*|| fmt[0].cfFormat == InternalNativeMethods.CF_HDROP*/) + return VSConstants.S_OK; + } + + return VSConstants.S_FALSE; + } + + void IDataObject.SetData(FORMATETC[] fmt, STGMEDIUM[] m, int fRelease) + { + } + #endregion + } + + [SecurityPermissionAttribute(SecurityAction.Demand, Flags = SecurityPermissionFlag.UnmanagedCode)] + internal static class DragDropHelper + { +#pragma warning disable 414 + internal static readonly ushort CF_VSREFPROJECTITEMS; + internal static readonly ushort CF_VSSTGPROJECTITEMS; + internal static readonly ushort CF_VSPROJECTCLIPDESCRIPTOR; +#pragma warning restore 414 + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + static DragDropHelper() + { + CF_VSREFPROJECTITEMS = UnsafeNativeMethods.RegisterClipboardFormat("CF_VSREFPROJECTITEMS"); + CF_VSSTGPROJECTITEMS = UnsafeNativeMethods.RegisterClipboardFormat("CF_VSSTGPROJECTITEMS"); + CF_VSPROJECTCLIPDESCRIPTOR = UnsafeNativeMethods.RegisterClipboardFormat("CF_PROJECTCLIPBOARDDESCRIPTOR"); + } + + + public static FORMATETC CreateFormatEtc(ushort iFormat) + { + FORMATETC fmt = new FORMATETC(); + fmt.cfFormat = iFormat; + fmt.ptd = IntPtr.Zero; + fmt.dwAspect = (uint)DVASPECT.DVASPECT_CONTENT; + fmt.lindex = -1; + fmt.tymed = (uint)TYMED.TYMED_HGLOBAL; + return fmt; + } + + public static int QueryGetData(Microsoft.VisualStudio.OLE.Interop.IDataObject pDataObject, ref FORMATETC fmtetc) + { + int returnValue = VSConstants.E_FAIL; + FORMATETC[] af = new FORMATETC[1]; + af[0] = fmtetc; + try + { + int result = ErrorHandler.ThrowOnFailure(pDataObject.QueryGetData(af)); + if(result == VSConstants.S_OK) + { + fmtetc = af[0]; + returnValue = VSConstants.S_OK; + } + } + catch(COMException e) + { + Trace.WriteLine("COMException : " + e.Message); + returnValue = e.ErrorCode; + } + + return returnValue; + } + + public static STGMEDIUM GetData(Microsoft.VisualStudio.OLE.Interop.IDataObject pDataObject, ref FORMATETC fmtetc) + { + FORMATETC[] af = new FORMATETC[1]; + af[0] = fmtetc; + STGMEDIUM[] sm = new STGMEDIUM[1]; + pDataObject.GetData(af, sm); + fmtetc = af[0]; + return sm[0]; + } + + /// + /// Retrives data from a VS format. + /// + public static List GetDroppedFiles(ushort format, Microsoft.VisualStudio.OLE.Interop.IDataObject dataObject, out DropDataType ddt) + { + ddt = DropDataType.None; + List droppedFiles = new List(); + + // try HDROP + FORMATETC fmtetc = CreateFormatEtc(format); + + if(QueryGetData(dataObject, ref fmtetc) == VSConstants.S_OK) + { + STGMEDIUM stgmedium = DragDropHelper.GetData(dataObject, ref fmtetc); + if(stgmedium.tymed == (uint)TYMED.TYMED_HGLOBAL) + { + // We are releasing the cloned hglobal here. + IntPtr dropInfoHandle = stgmedium.unionmember; + if(dropInfoHandle != IntPtr.Zero) + { + ddt = DropDataType.Shell; + try + { + uint numFiles = UnsafeNativeMethods.DragQueryFile(dropInfoHandle, 0xFFFFFFFF, null, 0); + + // We are a directory based project thus a projref string is placed on the clipboard. + // We assign the maximum length of a projref string. + // The format of a projref is : || + uint lenght = (uint)Guid.Empty.ToString().Length + 2 * NativeMethods.MAX_PATH + 2; + char[] moniker = new char[lenght + 1]; + for(uint fileIndex = 0; fileIndex < numFiles; fileIndex++) + { + uint queryFileLength = UnsafeNativeMethods.DragQueryFile(dropInfoHandle, fileIndex, moniker, lenght); + string filename = new String(moniker, 0, (int)queryFileLength); + droppedFiles.Add(filename); + } + } + finally + { + Marshal.FreeHGlobal(dropInfoHandle); + } + } + } + } + + return droppedFiles; + } + + + + public static string GetSourceProjectPath(Microsoft.VisualStudio.OLE.Interop.IDataObject dataObject) + { + string projectPath = null; + FORMATETC fmtetc = CreateFormatEtc(CF_VSPROJECTCLIPDESCRIPTOR); + + if(QueryGetData(dataObject, ref fmtetc) == VSConstants.S_OK) + { + STGMEDIUM stgmedium = DragDropHelper.GetData(dataObject, ref fmtetc); + if(stgmedium.tymed == (uint)TYMED.TYMED_HGLOBAL) + { + // We are releasing the cloned hglobal here. + IntPtr dropInfoHandle = stgmedium.unionmember; + if(dropInfoHandle != IntPtr.Zero) + { + try + { + string path = GetData(dropInfoHandle); + + // Clone the path that we can release our memory. + if(!String.IsNullOrEmpty(path)) + { + projectPath = String.Copy(path); + } + } + finally + { + Marshal.FreeHGlobal(dropInfoHandle); + } + } + } + } + + return projectPath; + } + + /// + /// Returns the data packed after the DROPFILES structure. + /// + /// + /// + internal static string GetData(IntPtr dropHandle) + { + IntPtr data = UnsafeNativeMethods.GlobalLock(dropHandle); + try + { + _DROPFILES df = (_DROPFILES)Marshal.PtrToStructure(data, typeof(_DROPFILES)); + if(df.fWide != 0) + { + IntPtr pdata = new IntPtr((long)data + df.pFiles); + return Marshal.PtrToStringUni(pdata); + } + } + finally + { + if(data != null) + { + UnsafeNativeMethods.GlobalUnLock(data); + } + } + + return null; + } + + internal static IntPtr CopyHGlobal(IntPtr data) + { + IntPtr src = UnsafeNativeMethods.GlobalLock(data); + int size = UnsafeNativeMethods.GlobalSize(data); + IntPtr ptr = Marshal.AllocHGlobal(size); + IntPtr buffer = UnsafeNativeMethods.GlobalLock(ptr); + + try + { + for(int i = 0; i < size; i++) + { + byte val = Marshal.ReadByte(new IntPtr((long)src + i)); + + Marshal.WriteByte(new IntPtr((long)buffer + i), val); + } + } + finally + { + if(buffer != IntPtr.Zero) + { + UnsafeNativeMethods.GlobalUnLock(buffer); + } + + if(src != IntPtr.Zero) + { + UnsafeNativeMethods.GlobalUnLock(src); + } + } + return ptr; + } + + internal static void CopyStringToHGlobal(string s, IntPtr data, int bufferSize) + { + Int16 nullTerminator = 0; + int dwSize = Marshal.SizeOf(nullTerminator); + + if((s.Length + 1) * Marshal.SizeOf(s[0]) > bufferSize) + throw new System.IO.InternalBufferOverflowException(); + // IntPtr memory already locked... + for(int i = 0, len = s.Length; i < len; i++) + { + Marshal.WriteInt16(data, i * dwSize, s[i]); + } + // NULL terminate it + Marshal.WriteInt16(new IntPtr((long)data + (s.Length * dwSize)), nullTerminator); + } + + } // end of dragdrophelper + + internal class EnumSTATDATA : IEnumSTATDATA + { + IEnumerable i; + + IEnumerator e; + + public EnumSTATDATA(IEnumerable i) + { + this.i = i; + this.e = i.GetEnumerator(); + } + + void IEnumSTATDATA.Clone(out IEnumSTATDATA clone) + { + clone = new EnumSTATDATA(i); + } + + int IEnumSTATDATA.Next(uint celt, STATDATA[] d, out uint fetched) + { + uint rc = 0; + //uint size = (fetched != null) ? fetched[0] : 0; + for(uint i = 0; i < celt; i++) + { + if(e.MoveNext()) + { + STATDATA sdata = (STATDATA)e.Current; + + rc++; + if(d != null && d.Length > i) + { + d[i] = sdata; + } + } + } + + fetched = rc; + return 0; + } + + int IEnumSTATDATA.Reset() + { + e.Reset(); + return 0; + } + + int IEnumSTATDATA.Skip(uint celt) + { + for(uint i = 0; i < celt; i++) + { + e.MoveNext(); + } + + return 0; + } + } + + internal class EnumFORMATETC : IEnumFORMATETC + { + IEnumerable cache; // of DataCacheEntrys. + + DATADIR dir; + + IEnumerator e; + + public EnumFORMATETC(DATADIR dir, IEnumerable cache) + { + this.cache = cache; + this.dir = dir; + e = cache.GetEnumerator(); + } + + void IEnumFORMATETC.Clone(out IEnumFORMATETC clone) + { + clone = new EnumFORMATETC(dir, cache); + } + + int IEnumFORMATETC.Next(uint celt, FORMATETC[] d, uint[] fetched) + { + uint rc = 0; + //uint size = (fetched != null) ? fetched[0] : 0; + for(uint i = 0; i < celt; i++) + { + if(e.MoveNext()) + { + DataCacheEntry entry = (DataCacheEntry)e.Current; + + rc++; + if(d != null && d.Length > i) + { + d[i] = entry.Format; + } + } + else + { + return VSConstants.S_FALSE; + } + } + + if(fetched != null && fetched.Length > 0) + fetched[0] = rc; + return VSConstants.S_OK; + } + + int IEnumFORMATETC.Reset() + { + e.Reset(); + return 0; + } + + int IEnumFORMATETC.Skip(uint celt) + { + for(uint i = 0; i < celt; i++) + { + e.MoveNext(); + } + + return 0; + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/DependentFileNode.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/DependentFileNode.cs new file mode 100644 index 0000000000..ae29bb9f37 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/DependentFileNode.cs @@ -0,0 +1,149 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.VisualStudio; +using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants; +using VsCommands = Microsoft.VisualStudio.VSConstants.VSStd97CmdID; +using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Defines the logic for all dependent file nodes (solution explorer icon, commands etc.) + /// + [CLSCompliant(false)] + [ComVisible(true)] + public class DependentFileNode : FileNode + { + #region fields + /// + /// Defines if the node has a name relation to its parent node + /// e.g. Form1.ext and Form1.resx are name related (until first occurence of extention separator) + /// + #endregion + + #region Properties + public override int ImageIndex + { + get { return (this.CanShowDefaultIcon() ? (int)ProjectNode.ImageName.DependentFile : (int)ProjectNode.ImageName.MissingFile); } + } + #endregion + + #region ctor + /// + /// Constructor for the DependentFileNode + /// + /// Root of the hierarchy + /// Associated project element + public DependentFileNode(ProjectNode root, ProjectElement element) + : base(root, element) + { + this.HasParentNodeNameRelation = false; + } + + + #endregion + + #region overridden methods + /// + /// Disable rename + /// + /// new label + /// E_NOTIMPLE in order to tell the call that we do not support rename + public override string GetEditLabel() + { + throw new NotImplementedException(); + } + + /// + /// Gets a handle to the icon that should be set for this node + /// + /// Whether the folder is open, ignored here. + /// Handle to icon for the node + public override object GetIconHandle(bool open) + { + return this.ProjectMgr.ImageHandler.GetIconHandle(this.ImageIndex); + } + + /// + /// Disable certain commands for dependent file nodes + /// + protected override int QueryStatusOnNode(Guid cmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result) + { + if(cmdGroup == VsMenus.guidStandardCommandSet97) + { + switch((VsCommands)cmd) + { + case VsCommands.Copy: + case VsCommands.Paste: + case VsCommands.Cut: + case VsCommands.Rename: + result |= QueryStatusResult.NOTSUPPORTED; + return VSConstants.S_OK; + + case VsCommands.ViewCode: + case VsCommands.Open: + case VsCommands.OpenWith: + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + } + } + else if(cmdGroup == VsMenus.guidStandardCommandSet2K) + { + if((VsCommands2K)cmd == VsCommands2K.EXCLUDEFROMPROJECT) + { + result |= QueryStatusResult.NOTSUPPORTED; + return VSConstants.S_OK; + } + } + else + { + return (int)OleConstants.OLECMDERR_E_UNKNOWNGROUP; + } + return base.QueryStatusOnNode(cmdGroup, cmd, pCmdText, ref result); + } + + /// + /// DependentFileNodes node cannot be dragged. + /// + /// null + protected internal override StringBuilder PrepareSelectedNodesForClipBoard() + { + return null; + } + + protected override NodeProperties CreatePropertiesObject() + { + return new DependentFileNodeProperties(this); + } + + /// + /// Redraws the state icon if the node is not excluded from source control. + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")] + protected internal override void UpdateSccStateIcons() + { + if(!this.ExcludeNodeFromScc) + { + this.Parent.ReDraw(UIHierarchyElement.SccState); + } + } + #endregion + + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/DesignPropertyDescriptor.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/DesignPropertyDescriptor.cs new file mode 100644 index 0000000000..8c4daea992 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/DesignPropertyDescriptor.cs @@ -0,0 +1,215 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.ComponentModel; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// The purpose of DesignPropertyDescriptor is to allow us to customize the + /// display name of the property in the property grid. None of the CLR + /// implementations of PropertyDescriptor allow you to change the DisplayName. + /// + public class DesignPropertyDescriptor : PropertyDescriptor + { + private string displayName; // Custom display name + private PropertyDescriptor property; // Base property descriptor + private Hashtable editors = new Hashtable(); // Type -> editor instance + private TypeConverter converter; + + + /// + /// Delegates to base. + /// + public override string DisplayName + { + get + { + return this.displayName; + } + } + + /// + /// Delegates to base. + /// + public override Type ComponentType + { + get + { + return this.property.ComponentType; + } + } + + /// + /// Delegates to base. + /// + public override bool IsReadOnly + { + get + { + return this.property.IsReadOnly; + } + } + + /// + /// Delegates to base. + /// + public override Type PropertyType + { + get + { + return this.property.PropertyType; + } + } + + + /// + /// Delegates to base. + /// + public override object GetEditor(Type editorBaseType) + { + object editor = this.editors[editorBaseType]; + if(editor == null) + { + for(int i = 0; i < this.Attributes.Count; i++) + { + EditorAttribute attr = Attributes[i] as EditorAttribute; + if(attr == null) + { + continue; + } + Type editorType = Type.GetType(attr.EditorBaseTypeName); + if(editorBaseType == editorType) + { + Type type = GetTypeFromNameProperty(attr.EditorTypeName); + if(type != null) + { + editor = CreateInstance(type); + this.editors[type] = editor; // cache it + break; + } + } + } + } + return editor; + } + + + /// + /// Return type converter for property + /// + public override TypeConverter Converter + { + get + { + if(converter == null) + { + PropertyPageTypeConverterAttribute attr = (PropertyPageTypeConverterAttribute)Attributes[typeof(PropertyPageTypeConverterAttribute)]; + if(attr != null && attr.ConverterType != null) + { + converter = (TypeConverter)CreateInstance(attr.ConverterType); + } + + if(converter == null) + { + converter = TypeDescriptor.GetConverter(this.PropertyType); + } + } + return converter; + } + } + + + + /// + /// Convert name to a Type object. + /// + public virtual Type GetTypeFromNameProperty(string typeName) + { + return Type.GetType(typeName); + } + + + /// + /// Delegates to base. + /// + public override bool CanResetValue(object component) + { + bool result = this.property.CanResetValue(component); + return result; + } + + /// + /// Delegates to base. + /// + public override object GetValue(object component) + { + object value = this.property.GetValue(component); + return value; + } + + /// + /// Delegates to base. + /// + public override void ResetValue(object component) + { + this.property.ResetValue(component); + } + + /// + /// Delegates to base. + /// + public override void SetValue(object component, object value) + { + this.property.SetValue(component, value); + } + + /// + /// Delegates to base. + /// + public override bool ShouldSerializeValue(object component) + { + bool result = this.property.ShouldSerializeValue(component); + return result; + } + + /// + /// Constructor. Copy the base property descriptor and also hold a pointer + /// to it for calling its overridden abstract methods. + /// + public DesignPropertyDescriptor(PropertyDescriptor prop) + : base(prop) + { + if (prop == null) + { + throw new ArgumentNullException("prop"); + } + + this.property = prop; + + DisplayNameAttribute attr = prop.Attributes[typeof(DisplayNameAttribute)] as DisplayNameAttribute; + + if(attr != null) + { + this.displayName = attr.DisplayName; + } + else + { + this.displayName = prop.Name; + } + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/AutomationClasses.cd b/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/AutomationClasses.cd new file mode 100644 index 0000000000..cfae1935d1 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/AutomationClasses.cd @@ -0,0 +1,90 @@ + + + + + + AAAAAJAAAAAAMAAAAAAAAAAAAAAAAAACgBABAAAAAAA= + Automation\OAFileItem.cs + + + + + + AEQAAAAAEABACBAAAAgASAQkCAgAIAAAAgAAAAAAACQ= + Automation\OANavigableProjectItems.cs + + + + + + + AAQAAJQAAABEUIIhQAAcCAQAAAkAAAAAIBgFCABCAAA= + Automation\OAProject.cs + + + + + + + + + + AQQAQJAAAABEYAAgQACMDCQBCAkAAAACgBAFAAECAAA= + Automation\OAProjectItem.cs + + + + + + + AEAAAAAAEAAAAAAAAAAgAAAggAAAAAAAAAAAAAAAACA= + Automation\OAProjectItems.cs + + + + + + EAYAAAACABAAABAAIAAAQCQEAAIAAAAIAAAAAAECAAA= + Automation\OAProperties.cs + + + + + + + AQSAAAAAABAAQAJgAAAQQAQAgAAAAAAAAAAgAAAAAAA= + Automation\OAProperty.cs + + + + + + + AAAAABAAAAAAAAAAAAAAACAAAAAACAAAAAAAAAAAAAA= + Automation\OANestedProjectItem.cs + + + + + + AAAAABAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQ= + Automation\OAReferenceFolderItem.cs + + + + + + AUQAAAAAAAAAAAAAAAAAQAAgAAIAAAAAAAQAAAAEAAA= + Automation\OASolutionFolder.cs + + + + + + + AAAAAAAAAAAAYAAAAAAAAAQAAAEAAAAAAAAAAAAAAAA= + Automation\OAReferenceItem.cs + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/ConfigurationClasses.cd b/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/ConfigurationClasses.cd new file mode 100644 index 0000000000..55aa6fbafd --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/ConfigurationClasses.cd @@ -0,0 +1,59 @@ + + + + + + AAAAAQQAAAAAAAAAAAAAgAAACAgAQBAAAAAAAACAABA= + Output.cs + + + + + + + AAAAIgABgwTAAAAAAAAAiAAAAIgEQAAggAAAEASAABA= + OutputGroup.cs + + + + + + + AAgCEEJABBAAkaSFCGCkAAEAAEiBYEEKACAAGRDJUEA= + ProjectConfig.cs + + + + + + + AABCAAAACgAACAAAAABABAEIAAQAgAAAIABAEABQAAA= + ProjectConfig.cs + + + + + + + gAWAgFQBAAJIAhACAAgCABAEAQ5MAAAABCAEEAAAEQQ= + ConfigProvider.cs + + + + + + + AgAEAEACAAAAAAQApAQAAgAUABAAA4AAQAIAAAAAgiA= + ProjectElement.cs + + + + + + PcP89b7sGr3/n1KfHv7+dr/f9h//71+3/xPdS+fRan4= + ProjectNode.CopyPaste.cs + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/DocumentManagerClasses.cd b/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/DocumentManagerClasses.cd new file mode 100644 index 0000000000..3504bb6c7d --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/DocumentManagerClasses.cd @@ -0,0 +1,39 @@ + + + + + + + FileDocumentManager.cs + AACAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + + + + + + DocumentManager.cs + AQCAQIQAAAAAIAEAAAAAAAAAIAAAAAAAAAAAACGAAAE= + + + + + + + + + + + + ProjectDesignerDocumentManager.cs + AACAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + + + + + + HierarchyNode.cs + NKl4a26oCodRuoDQodAKZKQQxKtwgtmAOJLYAHLHccE= + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/HierarchyClasses.cd b/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/HierarchyClasses.cd new file mode 100644 index 0000000000..9b560dd782 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/HierarchyClasses.cd @@ -0,0 +1,78 @@ + + + + + + BolBCECAAgFAAIEAgBACZAAAAAAAAAAQyAQQAIRGQIA= + FileNode.cs + + + + + + PcP89b7sGr3/n1KfHv7+dr/f9h//71+3/xPdS+fRan4= + ProjectNode.CopyPaste.cs + + + + + + + NKl4K26oqqdRuqD4sdAKZLc0zKtxgtmgOJLYADLHccE= + HierarchyNode.cs + + + + + + + AABFCBAAAAYgAAAgQAAAAAAEQMABAAAATAWAAAABAAE= + ProjectContainerNode.cs + + + + + + + BIAQEAKAAgFAAAAAgIACJAAABAICAAAACAAQAAACAQA= + FolderNode.cs + + + + + + AIBAAGagAAFAAAAAgACiIAAAAAAQAAEAGAAQAABAAAA= + ReferenceNode.cs + + + + + + AABICACgAAUAAIAgggACIABAAAYQQAAAGEIQAABAARA= + ReferenceContainerNode.cs + + + + + + + AACACCIAAAIAACQgQAAEBAQAgACAAAABCAgQAQACIAA= + ComReferenceNode.cs + + + + + + CIACCFYCAgAAgAEgAIACBAQAAAAQABAACAAAAECACgA= + ProjectReferenceNode.cs + + + + + + AAACCCJAACAACAAgEAoAAAACBABACIAIGAAAEACAABw= + AssemblyReferenceNode.cs + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/PropertiesClasses.cd b/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/PropertiesClasses.cd new file mode 100644 index 0000000000..25c96951b6 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/PropertiesClasses.cd @@ -0,0 +1,69 @@ + + + + + + AAAACAAACAAAAAAgAAAAgAAAABAEIAAAAERAAAAACAA= + LocalizableProperties.cs + + + + + + + CAAAAAAgAAAAAAAAAAAAAAAAAAAAIQAAAAAAAABAAAA= + NodeProperties.cs + + + + + + CAAAAAAAAAAAAAAAAAAAAAQAAAAAIAAAAAAAAAAAAAA= + NodeProperties.cs + + + + + + EQACAAAAAAAAAQAAAAAECAQAAAkgAggAAAhECAEAAAA= + NodeProperties.cs + + + + + + + CAAAAAAgAAAAAAAAAAAAAAAAAAAAIAAAAAAAAABAAAA= + NodeProperties.cs + + + + + + AAABhAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAQAAgQAYA= + NodeProperties.cs + + + + + + CAAAAAAAABAAAAAAAAAAAAAAAAAgIAgAAAQAAABAAAA= + NodeProperties.cs + + + + + + CAAAAAAABAAAAAAAAAAAAAAAAAAAIAAAAAAAAABAAAA= + NodeProperties.cs + + + + + + CAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA= + NodeProperties.cs + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/ReferenceClasses.cd b/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/ReferenceClasses.cd new file mode 100644 index 0000000000..4c8e322c10 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Diagrams/ReferenceClasses.cd @@ -0,0 +1,77 @@ + + + + + + + + ReferenceNode.cs + + + + + AIBAAGagAAFAAAAAgACiIAAAAAAQAAEAGAAQAABAAAA= + ReferenceNode.cs + + + + + + + + + + + + + + + CIACCFYCAgAAgAEgAIACBAQAAAAQABAACAAAAECACgA= + ProjectReferenceNode.cs + + + + + + + + + + + + AAAQAAYAAAQAgAAQAEAAAAAAAAAAAAAAEQAAAACAAAA= + BuildDependency.cs + + + + + + + AACACCIAAAIAACQgQAAEBAQAgACAAAABCAgQAQACIAA= + ComReferenceNode.cs + + + + + + AAAAAAAAAAAAAAAAAQAAIAAAAgAAAAAIAAAAgABAAAA= + SolutionListenerForProjectReferenceUpdate.cs + + + + + + AAIACRBQoCAQCCAAEAIAJAAACgQBAAAYAQAiiAIAAAA= + SolutionListener.cs + + + + + + + AAACCCJAACAACAAgEAoAAAACBABACIAIGAAAEACAABw= + AssemblyReferenceNode.cs + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/DocumentManager.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/DocumentManager.cs new file mode 100644 index 0000000000..7bb13d0ac0 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/DocumentManager.cs @@ -0,0 +1,349 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using IServiceProvider = System.IServiceProvider; +using ShellConstants = Microsoft.VisualStudio.Shell.Interop.Constants; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// This abstract class handles opening, saving of items in the hierarchy. + /// + [CLSCompliant(false)] + public abstract class DocumentManager + { + #region fields + private HierarchyNode node = null; + #endregion + + #region properties + protected HierarchyNode Node + { + get + { + return this.node; + } + } + #endregion + + #region ctors + protected DocumentManager(HierarchyNode node) + { + this.node = node; + } + #endregion + + #region virtual methods + + /// + /// Open a document using the standard editor. This method has no implementation since a document is abstract in this context + /// + /// In MultiView case determines view to be activated by IVsMultiViewDocumentView. For a list of logical view GUIDS, see constants starting with LOGVIEWID_ defined in NativeMethods class + /// IntPtr to the IUnknown interface of the existing document data object + /// A reference to the window frame that is mapped to the document + /// Determine the UI action on the document window + /// NotImplementedException + /// See FileDocumentManager class for an implementation of this method + public virtual int Open(ref Guid logicalView, IntPtr docDataExisting, out IVsWindowFrame windowFrame, WindowFrameShowAction windowFrameAction) + { + throw new NotImplementedException(); + } + + /// + /// Open a document using a specific editor. This method has no implementation. + /// + /// Specifies actions to take when opening a specific editor. Possible editor flags are defined in the enumeration Microsoft.VisualStudio.Shell.Interop.__VSOSPEFLAGS + /// Unique identifier of the editor type + /// Name of the physical view. If null, the environment calls MapLogicalView on the editor factory to determine the physical view that corresponds to the logical view. In this case, null does not specify the primary view, but rather indicates that you do not know which view corresponds to the logical view + /// In MultiView case determines view to be activated by IVsMultiViewDocumentView. For a list of logical view GUIDS, see constants starting with LOGVIEWID_ defined in NativeMethods class + /// IntPtr to the IUnknown interface of the existing document data object + /// A reference to the window frame that is mapped to the document + /// Determine the UI action on the document window + /// NotImplementedException + /// See FileDocumentManager for an implementation of this method + public virtual int OpenWithSpecific(uint editorFlags, ref Guid editorType, string physicalView, ref Guid logicalView, IntPtr docDataExisting, out IVsWindowFrame frame, WindowFrameShowAction windowFrameAction) + { + throw new NotImplementedException(); + } + + /// + /// Close an open document window + /// + /// Decides how to close the document + /// S_OK if successful, otherwise an error is returned + public virtual int Close(__FRAMECLOSE closeFlag) + { + if(this.node == null || this.node.ProjectMgr == null || this.node.ProjectMgr.IsClosed) + { + return VSConstants.E_FAIL; + } + + // Get info about the document + bool isDirty, isOpen, isOpenedByUs; + uint docCookie; + IVsPersistDocData ppIVsPersistDocData; + this.GetDocInfo(out isOpen, out isDirty, out isOpenedByUs, out docCookie, out ppIVsPersistDocData); + + if(isOpenedByUs) + { + IVsUIShellOpenDocument shell = this.Node.ProjectMgr.Site.GetService(typeof(IVsUIShellOpenDocument)) as IVsUIShellOpenDocument; + Guid logicalView = Guid.Empty; + uint grfIDO = 0; + IVsUIHierarchy pHierOpen; + uint[] itemIdOpen = new uint[1]; + IVsWindowFrame windowFrame; + int fOpen; + ErrorHandler.ThrowOnFailure(shell.IsDocumentOpen(this.Node.ProjectMgr, this.Node.ID, this.Node.Url, ref logicalView, grfIDO, out pHierOpen, itemIdOpen, out windowFrame, out fOpen)); + + if(windowFrame != null) + { + docCookie = 0; + return windowFrame.CloseFrame((uint)closeFlag); + } + } + + return VSConstants.S_OK; + } + + /// + /// Silently saves an open document + /// + /// Save the open document only if it is dirty + /// The call to SaveDocData may return Microsoft.VisualStudio.Shell.Interop.PFF_RESULTS.STG_S_DATALOSS to indicate some characters could not be represented in the current codepage + public virtual void Save(bool saveIfDirty) + { + bool isDirty, isOpen, isOpenedByUs; + uint docCookie; + IVsPersistDocData persistDocData; + this.GetDocInfo(out isOpen, out isDirty, out isOpenedByUs, out docCookie, out persistDocData); + if(isDirty && saveIfDirty && persistDocData != null) + { + string name; + int cancelled; + ErrorHandler.ThrowOnFailure(persistDocData.SaveDocData(VSSAVEFLAGS.VSSAVE_SilentSave, out name, out cancelled)); + } + } + + #endregion + + #region helper methods + /// + /// Get document properties from RDT + /// + internal void GetDocInfo( + out bool isOpen, // true if the doc is opened + out bool isDirty, // true if the doc is dirty + out bool isOpenedByUs, // true if opened by our project + out uint docCookie, // VSDOCCOOKIE if open + out IVsPersistDocData persistDocData) + { + isOpen = isDirty = isOpenedByUs = false; + docCookie = (uint)ShellConstants.VSDOCCOOKIE_NIL; + persistDocData = null; + + if(this.node == null || this.node.ProjectMgr == null || this.node.ProjectMgr.IsClosed) + { + return; + } + + IVsHierarchy hierarchy; + uint vsitemid = VSConstants.VSITEMID_NIL; + + VsShellUtilities.GetRDTDocumentInfo(this.node.ProjectMgr.Site, this.node.Url, out hierarchy, out vsitemid, out persistDocData, out docCookie); + + if(hierarchy == null || docCookie == (uint)ShellConstants.VSDOCCOOKIE_NIL) + { + return; + } + + isOpen = true; + // check if the doc is opened by another project + if(Utilities.IsSameComObject(this.node.ProjectMgr, hierarchy)) + { + isOpenedByUs = true; + } + + if(persistDocData != null) + { + int isDocDataDirty; + ErrorHandler.ThrowOnFailure(persistDocData.IsDocDataDirty(out isDocDataDirty)); + isDirty = (isDocDataDirty != 0); + } + } + + protected string GetOwnerCaption() + { + Debug.Assert(this.node != null, "No node has been initialized for the document manager"); + + object pvar; + ErrorHandler.ThrowOnFailure(this.node.GetProperty(this.node.ID, (int)__VSHPROPID.VSHPROPID_Caption, out pvar)); + + return (pvar as string); + } + + protected static void CloseWindowFrame(ref IVsWindowFrame windowFrame) + { + if(windowFrame != null) + { + try + { + ErrorHandler.ThrowOnFailure(windowFrame.CloseFrame(0)); + } + finally + { + windowFrame = null; + } + } + } + + protected string GetFullPathForDocument() + { + string fullPath = String.Empty; + + Debug.Assert(this.node != null, "No node has been initialized for the document manager"); + + // Get the URL representing the item + fullPath = this.node.GetMkDocument(); + + Debug.Assert(!String.IsNullOrEmpty(fullPath), "Could not retrive the fullpath for the node" + this.Node.ID.ToString(CultureInfo.CurrentCulture)); + return fullPath; + } + + #endregion + + #region static methods + /// + /// Updates the caption for all windows associated to the document. + /// + /// The service provider. + /// The new caption. + /// The IUnknown interface to a document data object associated with a registered document. + public static void UpdateCaption(IServiceProvider site, string caption, IntPtr docData) + { + if(site == null) + { + throw new ArgumentNullException("site"); + } + + if(String.IsNullOrEmpty(caption)) + { + throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "caption"); + } + + IVsUIShell uiShell = site.GetService(typeof(SVsUIShell)) as IVsUIShell; + + // We need to tell the windows to update their captions. + IEnumWindowFrames windowFramesEnum; + ErrorHandler.ThrowOnFailure(uiShell.GetDocumentWindowEnum(out windowFramesEnum)); + IVsWindowFrame[] windowFrames = new IVsWindowFrame[1]; + uint fetched; + while(windowFramesEnum.Next(1, windowFrames, out fetched) == VSConstants.S_OK && fetched == 1) + { + IVsWindowFrame windowFrame = windowFrames[0]; + object data; + ErrorHandler.ThrowOnFailure(windowFrame.GetProperty((int)__VSFPROPID.VSFPROPID_DocData, out data)); + IntPtr ptr = Marshal.GetIUnknownForObject(data); + try + { + if(ptr == docData) + { + ErrorHandler.ThrowOnFailure(windowFrame.SetProperty((int)__VSFPROPID.VSFPROPID_OwnerCaption, caption)); + } + } + finally + { + if(ptr != IntPtr.Zero) + { + Marshal.Release(ptr); + } + } + } + } + + /// + /// Rename document in the running document table from oldName to newName. + /// + /// The service provider. + /// Full path to the old name of the document. + /// Full path to the new name of the document. + /// The new item id of the document + public static void RenameDocument(IServiceProvider site, string oldName, string newName, uint newItemId) + { + if(site == null) + { + throw new ArgumentNullException("site"); + } + + if(String.IsNullOrEmpty(oldName)) + { + throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "oldName"); + } + + if(String.IsNullOrEmpty(newName)) + { + throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "newName"); + } + + if(newItemId == VSConstants.VSITEMID_NIL) + { + throw new ArgumentNullException("newItemId"); + } + + IVsRunningDocumentTable pRDT = site.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + IVsUIShellOpenDocument doc = site.GetService(typeof(SVsUIShellOpenDocument)) as IVsUIShellOpenDocument; + + if(pRDT == null || doc == null) return; + + IVsHierarchy pIVsHierarchy; + uint itemId; + IntPtr docData; + uint uiVsDocCookie; + ErrorHandler.ThrowOnFailure(pRDT.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, oldName, out pIVsHierarchy, out itemId, out docData, out uiVsDocCookie)); + + if(docData != IntPtr.Zero) + { + try + { + IntPtr pUnk = Marshal.GetIUnknownForObject(pIVsHierarchy); + Guid iid = typeof(IVsHierarchy).GUID; + IntPtr pHier; + Marshal.QueryInterface(pUnk, ref iid, out pHier); + try + { + ErrorHandler.ThrowOnFailure(pRDT.RenameDocument(oldName, newName, pHier, newItemId)); + } + finally + { + if(pHier != IntPtr.Zero) + Marshal.Release(pHier); + if(pUnk != IntPtr.Zero) + Marshal.Release(pUnk); + } + } + finally + { + Marshal.Release(docData); + } + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/DontShowAgainDialog.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/DontShowAgainDialog.cs new file mode 100644 index 0000000000..b92c4adcc6 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/DontShowAgainDialog.cs @@ -0,0 +1,347 @@ +/*************************************************************************** + +Copyright (c) Microsoft Corporation. All rights reserved. +This code is licensed under the Visual Studio SDK license terms. +THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. + +***************************************************************************/ +extern alias Shell10; +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Drawing; +using System.Globalization; +using System.Windows.Forms; +using System.Windows.Forms.Design; +using Shell10.Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.Win32; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Defines a genric don't show again dilaog. + /// + internal partial class DontShowAgainDialog : Form + { + #region constants + /// + /// Defines the General subkey under VS hive for the CurrentUser. + /// + private static string GeneralSubKey = "General"; + #endregion + + #region fields + /// + /// Defines the bitmap to be drawn + /// + private Bitmap bitmap; + + /// + /// The associated service provider + /// + private IServiceProvider serviceProvider; + + /// + /// The help topic associated. + /// + private string helpTopic; + + /// + /// The value of the don't show again check box + /// + private bool dontShowAgainValue; + #endregion + + #region constructors + /// + /// Overloaded constructor + /// + /// The associated service provider. + /// Thetext to be shown on the dialog + /// The associated help topic + /// The default button + internal DontShowAgainDialog(IServiceProvider serviceProvider, string messageText, string helpTopic, DefaultButton button) + { + if(serviceProvider == null) + { + throw new ArgumentNullException("serviceProvider"); + } + + this.serviceProvider = serviceProvider; + this.InitializeComponent(); + + if(button == DefaultButton.OK) + { + this.AcceptButton = this.okButton; + } + else + { + this.AcceptButton = this.cancelButton; + } + + + this.SetupComponents(messageText, helpTopic); + } + #endregion + + #region properties + /// + /// The value of the dont' show again checkbox before the dialog is closed. + /// + internal bool DontShowAgainValue + { + get + { + return this.dontShowAgainValue; + } + } + #endregion + + #region methods + /// + /// Shows help for the help topic. + /// + protected virtual void ShowHelp() + { + Microsoft.VisualStudio.VSHelp.Help help = this.serviceProvider.GetService(typeof(Microsoft.VisualStudio.VSHelp.Help)) as Microsoft.VisualStudio.VSHelp.Help; + + if(help != null) + { + help.DisplayTopicFromF1Keyword(this.helpTopic); + } + } + + /// + /// Launches a DontShowAgainDialog if it is needed. + /// + /// An associated serviceprovider. + /// The text the dilaog box will contain. + /// The associated help topic. + /// The default button. + /// The registry key that serves for persisting the not show again value. + /// A Dialog result. + internal static DialogResult LaunchDontShowAgainDialog(IServiceProvider serviceProvider, string messageText, string helpTopic, DefaultButton button, string registryKey) + { + if(String.IsNullOrEmpty(registryKey)) + { + throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "registryKey"); + } + + DialogResult result = DialogResult.OK; + + bool dontShowAgain = ReadDontShowAgainValue(registryKey); + + if(!dontShowAgain) + { + DontShowAgainDialog dialog = new DontShowAgainDialog(serviceProvider, messageText, helpTopic, button); + result = dialog.ShowDialog(); + + // Now write to the registry the value. + if(dialog.DontShowAgainValue) + { + WriteDontShowAgainValue(registryKey, 1); + } + } + + return result; + } + + /// + /// Reads a boolean value specifying whether to show or not show the dialog + /// + /// The key containing the value. + /// The value read. If the value cannot be read false is returned. + internal static bool ReadDontShowAgainValue(string registryKey) + { + bool dontShowAgain = false; + using(RegistryKey root = VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_UserSettings)) + { + if(root != null) + { + using(RegistryKey key = root.OpenSubKey(GeneralSubKey)) + { + int value = (int)key.GetValue(registryKey, 0); + dontShowAgain = (value != 0); + } + } + } + + return dontShowAgain; + } + + /// + /// Writes a value 1 in the registrykey and as aresult the dont show again dialog will not be launched. + /// + /// The key to write to. + /// The value to write. + internal static void WriteDontShowAgainValue(string registryKey, int value) + { + using(RegistryKey root = VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_UserSettings)) + { + if(root != null) + { + using(RegistryKey key = root.OpenSubKey(GeneralSubKey, true)) + { + key.SetValue(registryKey, value, RegistryValueKind.DWord); + } + } + } + } + + /// + /// Shows the dialog if possible hosted by the IUIService. + /// + /// A DialogResult + internal new DialogResult ShowDialog() + { + Debug.Assert(this.serviceProvider != null, "The service provider should not be null at this time"); + IUIService uiService = this.serviceProvider.GetService(typeof(IUIService)) as IUIService; + if(uiService == null) + { + return this.ShowDialog(); + } + + return uiService.ShowDialog(this); + } + + /// + /// Defines the event delegate when help is requested. + /// + /// + /// + private void OnHelpRequested(object sender, HelpEventArgs hlpevent) + { + if(String.IsNullOrEmpty(this.helpTopic)) + { + return; + } + + this.ShowHelp(); + hlpevent.Handled = true; + } + + /// + /// Defines the delegate that responds to the help button clicked event. + /// + /// The sender of the event. + /// An instance of canceleventargs + private void OnHelpButtonClicked(object sender, CancelEventArgs e) + { + if(String.IsNullOrEmpty(this.helpTopic)) + { + return; + } + + e.Cancel = true; + this.ShowHelp(); + } + + /// + /// Called when the dialog box is repainted. + /// + /// The sender of the event. + /// The associated paint event args. + private void OnPaint(object sender, PaintEventArgs e) + { + e.Graphics.DrawImage(this.bitmap, new Point(7, this.messageText.Location.Y)); + } + + + /// + /// Sets up the components that are not done through teh Initialize components. + /// + /// The associated help topic + /// The message to show on the dilaog. + private void SetupComponents(string messageTextParam, string helpTopicParam) + { + // Compute the Distance to the bottom of the dialog + int distanceToBottom = this.Size.Height - this.cancelButton.Location.Y; + + // The Y end coordinate of the messageText before it assigned its value. + int deltaY = this.messageText.Location.Y + this.messageText.Size.Height; + + // Set the maximum size as the CancelButtonEndX - MessageTextStartX. This way it wil never pass by the button. + this.messageText.MaximumSize = new Size(this.cancelButton.Location.X + this.cancelButton.Size.Width - this.messageText.Location.X, 0); + this.messageText.Text = messageTextParam; + + // How much it has changed? + deltaY = this.messageText.Size.Height - deltaY; + this.AdjustSizesVertically(deltaY, distanceToBottom); + + if(String.IsNullOrEmpty(helpTopicParam)) + { + this.HelpButton = false; + } + else + { + this.helpTopic = helpTopicParam; + } + + // Create the system icon that will be drawn on the dialog page. + Icon icon = new Icon(SystemIcons.Exclamation, 40, 40); + + // Call ToBitmap to convert it. + this.bitmap = icon.ToBitmap(); + + this.CenterToScreen(); + } + + /// + /// Handles the cancel button clicked event. + /// + /// The sender of teh event. + /// The event args associated to teh event. + private void OnCancelButtonClicked(object sender, EventArgs e) + { + this.dontShowAgainValue = this.dontShowAgain.Checked; + this.DialogResult = DialogResult.Cancel; + } + + /// + /// Handles the cancel button clicked event. + /// + /// The sender of teh event. + /// The event args associated to teh event. + private void OnOKButtonClicked(object sender, EventArgs e) + { + this.dontShowAgainValue = this.dontShowAgain.Checked; + this.DialogResult = DialogResult.OK; + this.Close(); + } + + /// + /// Moves controls vertically because of a vertical change in the messagetext. + /// + private void AdjustSizesVertically(int deltaY, int distanceToBottom) + { + // Move the checkbox to its new location determined by the height the label. + this.dontShowAgain.Location = new Point(this.dontShowAgain.Location.X, this.dontShowAgain.Location.Y + deltaY); + + // Move the buttons to their new location; The X coordinate is fixed. + int newSizeY = this.cancelButton.Location.Y + deltaY; + this.cancelButton.Location = new Point(this.cancelButton.Location.X, newSizeY); + + newSizeY = this.okButton.Location.Y + deltaY; + this.okButton.Location = new Point(this.okButton.Location.X, newSizeY); + + // Now resize the dialog itself. + this.Size = new Size(this.Size.Width, this.cancelButton.Location.Y + distanceToBottom); + } + #endregion + + #region nested types + /// + /// Defines which button to serve as the default button. + /// + internal enum DefaultButton + { + OK, + Cancel, + } + #endregion + } +} \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/DontShowAgainDialog.designer.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/DontShowAgainDialog.designer.cs new file mode 100644 index 0000000000..4cd12e24c5 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/DontShowAgainDialog.designer.cs @@ -0,0 +1,108 @@ +namespace Microsoft.VisualStudio.Project +{ + partial class DontShowAgainDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + try + { + if(disposing) + { + if(components != null) + { + components.Dispose(); + } + + if(this.bitmap != null) + { + this.bitmap.Dispose(); + } + } + } + finally + { + base.Dispose(disposing); + } + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(DontShowAgainDialog)); + this.messageText = new System.Windows.Forms.Label(); + this.dontShowAgain = new System.Windows.Forms.CheckBox(); + this.okButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.SuspendLayout(); + // + // messageText + // + this.messageText.AutoEllipsis = true; + resources.ApplyResources(this.messageText, "messageText"); + this.messageText.Name = "messageText"; + // + // dontShowAgain + // + resources.ApplyResources(this.dontShowAgain, "dontShowAgain"); + this.dontShowAgain.Name = "dontShowAgain"; + this.dontShowAgain.UseVisualStyleBackColor = true; + // + // okButton + // + resources.ApplyResources(this.okButton, "okButton"); + this.okButton.Name = "okButton"; + this.okButton.UseVisualStyleBackColor = true; + this.okButton.Click += new System.EventHandler(this.OnOKButtonClicked); + // + // cancelButton + // + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + resources.ApplyResources(this.cancelButton, "cancelButton"); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.UseVisualStyleBackColor = true; + this.cancelButton.Click += new System.EventHandler(this.OnCancelButtonClicked); + // + // DontShowAgainDialog + // + this.AcceptButton = this.okButton; + this.CancelButton = this.cancelButton; + resources.ApplyResources(this, "$this"); + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.okButton); + this.Controls.Add(this.dontShowAgain); + this.Controls.Add(this.messageText); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.HelpButton = true; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "DontShowAgainDialog"; + this.HelpButtonClicked += new System.ComponentModel.CancelEventHandler(this.OnHelpButtonClicked); + this.Paint += new System.Windows.Forms.PaintEventHandler(this.OnPaint); + this.HelpRequested += new System.Windows.Forms.HelpEventHandler(this.OnHelpRequested); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label messageText; + private System.Windows.Forms.CheckBox dontShowAgain; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Button cancelButton; + } +} \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/DontShowAgainDialog.resx b/Tools/IronStudio/IronStudio/VisualStudio/Project/DontShowAgainDialog.resx new file mode 100644 index 0000000000..68a1e9c3aa --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/DontShowAgainDialog.resx @@ -0,0 +1,239 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + True + + + + 49, 9 + + + 16, 13 + + + 0 + + + M + + + messageText + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + True + + + 52, 47 + + + 172, 17 + + + 1 + + + Don't show this message again + + + dontShowAgain + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + 235, 69 + + + 75, 23 + + + 2 + + + OK + + + okButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 316, 69 + + + 75, 23 + + + 3 + + + Cancel + + + cancelButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 402, 105 + + + Microsoft Visual Studio + + + DontShowAgainDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/EngineShim.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/EngineShim.cs new file mode 100644 index 0000000000..3468184a98 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/EngineShim.cs @@ -0,0 +1,38 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Build.BuildEngine; + +namespace Microsoft.VisualStudio.Project { + + class EngineShim { + private Engine _engine; + + public EngineShim() + : this(new Engine()) { + } + + public EngineShim(Engine engine) { + _engine = engine; + } + + internal ProjectShim CreateNewProject() { + var project = _engine.CreateNewProject(); + if (project != null) { + return new ProjectShim(project); + } + return null; + } + + internal void UnloadProject(ProjectShim projectShim) { + _engine.UnloadProject(projectShim.Project); + } + } + + enum SecurityCheckPass { + Targets, + Properties, + Items, + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/EnumDependencies.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/EnumDependencies.cs new file mode 100644 index 0000000000..f48c0386e3 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/EnumDependencies.cs @@ -0,0 +1,108 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.VisualStudio.Project +{ + [CLSCompliant(false)] + public class EnumDependencies : IVsEnumDependencies + { + private List dependencyList = new List(); + + private uint nextIndex; + + public EnumDependencies(IList dependencyList) + { + if (dependencyList == null) + { + throw new ArgumentNullException("dependencyList"); + } + + foreach(IVsDependency dependency in dependencyList) + { + this.dependencyList.Add(dependency); + } + } + + public EnumDependencies(IList dependencyList) + { + if (dependencyList == null) + { + throw new ArgumentNullException("dependencyList"); + } + + foreach(IVsBuildDependency dependency in dependencyList) + { + this.dependencyList.Add(dependency); + } + } + + public int Clone(out IVsEnumDependencies enumDependencies) + { + enumDependencies = new EnumDependencies(this.dependencyList); + ErrorHandler.ThrowOnFailure(enumDependencies.Skip(this.nextIndex)); + return VSConstants.S_OK; + } + + public int Next(uint elements, IVsDependency[] dependencies, out uint elementsFetched) + { + elementsFetched = 0; + if (dependencies == null) + { + throw new ArgumentNullException("dependencies"); + } + + uint fetched = 0; + int count = this.dependencyList.Count; + + while(this.nextIndex < count && elements > 0 && fetched < count) + { + dependencies[fetched] = this.dependencyList[(int)this.nextIndex]; + this.nextIndex++; + fetched++; + elements--; + + } + + elementsFetched = fetched; + + // Did we get 'em all? + return (elements == 0 ? VSConstants.S_OK : VSConstants.S_FALSE); + } + + public int Reset() + { + this.nextIndex = 0; + return VSConstants.S_OK; + } + + public int Skip(uint elements) + { + this.nextIndex += elements; + uint count = (uint)this.dependencyList.Count; + + if(this.nextIndex > count) + { + this.nextIndex = count; + return VSConstants.S_FALSE; + } + + return VSConstants.S_OK; + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/FileChangeManager.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/FileChangeManager.cs new file mode 100644 index 0000000000..05f16fbb9b --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/FileChangeManager.cs @@ -0,0 +1,291 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Globalization; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using IServiceProvider = System.IServiceProvider; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// This object is in charge of reloading nodes that have file monikers that can be listened to changes + /// + internal class FileChangeManager : IVsFileChangeEvents + { + #region nested objects + /// + /// Defines a data structure that can link a item moniker to the item and its file change cookie. + /// + private struct ObservedItemInfo + { + /// + /// Defines the id of the item that is to be reloaded. + /// + private uint itemID; + + /// + /// Defines the file change cookie that is returned when listening on file changes on the nested project item. + /// + private uint fileChangeCookie; + + /// + /// Defines the nested project item that is to be reloaded. + /// + internal uint ItemID + { + get + { + return this.itemID; + } + + set + { + this.itemID = value; + } + } + + /// + /// Defines the file change cookie that is returned when listenning on file changes on the nested project item. + /// + internal uint FileChangeCookie + { + get + { + return this.fileChangeCookie; + } + + set + { + this.fileChangeCookie = value; + } + } + } + #endregion + + #region Fields + /// + /// Event that is raised when one of the observed file names have changed on disk. + /// + internal event EventHandler FileChangedOnDisk; + + /// + /// Reference to the FileChange service. + /// + private IVsFileChangeEx fileChangeService; + + /// + /// Maps between the observed item identified by its filename (in canonicalized form) and the cookie used for subscribing + /// to the events. + /// + private Dictionary observedItems = new Dictionary(); + + /// + /// Has Disposed already been called? + /// + private bool disposed; + #endregion + + #region Constructor + /// + /// Overloaded ctor. + /// + /// An instance of a project item. + internal FileChangeManager(IServiceProvider serviceProvider) + { + #region input validation + if(serviceProvider == null) + { + throw new ArgumentNullException("serviceProvider"); + } + #endregion + + this.fileChangeService = (IVsFileChangeEx)serviceProvider.GetService(typeof(SVsFileChangeEx)); + + if(this.fileChangeService == null) + { + // VS is in bad state, since the SVsFileChangeEx could not be proffered. + throw new InvalidOperationException(); + } + } + #endregion + + #region IDisposable Members + /// + /// Disposes resources. + /// + public void Dispose() + { + // Don't dispose more than once + if(this.disposed) + { + return; + } + + this.disposed = true; + + // Unsubscribe from the observed source files. + foreach(ObservedItemInfo info in this.observedItems.Values) + { + ErrorHandler.ThrowOnFailure(this.fileChangeService.UnadviseFileChange(info.FileChangeCookie)); + } + + // Clean the observerItems list + this.observedItems.Clear(); + } + #endregion + + #region IVsFileChangeEvents Members + /// + /// Called when one of the file have changed on disk. + /// + /// Number of files changed. + /// Array of file names. + /// Array of flags indicating the type of changes. See _VSFILECHANGEFLAGS. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + int IVsFileChangeEvents.FilesChanged(uint numberOfFilesChanged, string[] filesChanged, uint[] flags) + { + if (filesChanged == null) + { + throw new ArgumentNullException("filesChanged"); + } + + if (flags == null) + { + throw new ArgumentNullException("flags"); + } + + if(this.FileChangedOnDisk != null) + { + for(int i = 0; i < numberOfFilesChanged; i++) + { + string fullFileName = Utilities.CanonicalizeFileName(filesChanged[i]); + if(this.observedItems.ContainsKey(fullFileName)) + { + ObservedItemInfo info = this.observedItems[fullFileName]; + this.FileChangedOnDisk(this, new FileChangedOnDiskEventArgs(fullFileName, info.ItemID, (_VSFILECHANGEFLAGS)flags[i])); + } + } + } + + return VSConstants.S_OK; + } + + /// + /// Notifies clients of changes made to a directory. + /// + /// Name of the directory that had a change. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + int IVsFileChangeEvents.DirectoryChanged(string directory) + { + return VSConstants.S_OK; + } + #endregion + + #region helpers + /// + /// Observe when the given file is updated on disk. In this case we do not care about the item id that represents the file in the hierarchy. + /// + /// File to observe. + internal void ObserveItem(string fileName) + { + this.ObserveItem(fileName, VSConstants.VSITEMID_NIL); + } + + /// + /// Observe when the given file is updated on disk. + /// + /// File to observe. + /// The item id of the item to observe. + internal void ObserveItem(string fileName, uint id) + { + #region Input validation + if(String.IsNullOrEmpty(fileName)) + { + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "fileName"); + } + #endregion + + string fullFileName = Utilities.CanonicalizeFileName(fileName); + if(!this.observedItems.ContainsKey(fullFileName)) + { + // Observe changes to the file + uint fileChangeCookie; + ErrorHandler.ThrowOnFailure(this.fileChangeService.AdviseFileChange(fullFileName, (uint)(_VSFILECHANGEFLAGS.VSFILECHG_Time | _VSFILECHANGEFLAGS.VSFILECHG_Del), this, out fileChangeCookie)); + + ObservedItemInfo itemInfo = new ObservedItemInfo(); + itemInfo.ItemID = id; + itemInfo.FileChangeCookie = fileChangeCookie; + + // Remember that we're observing this file (used in FilesChanged event handler) + this.observedItems.Add(fullFileName, itemInfo); + } + } + + /// + /// Ignore item file changes for the specified item. + /// + /// File to ignore observing. + /// Flag indicating whether or not to ignore changes (1 to ignore, 0 to stop ignoring). + internal void IgnoreItemChanges(string fileName, bool ignore) + { + #region Input validation + if(String.IsNullOrEmpty(fileName)) + { + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "fileName"); + } + #endregion + + string fullFileName = Utilities.CanonicalizeFileName(fileName); + if(this.observedItems.ContainsKey(fullFileName)) + { + // Call ignore file with the flags specified. + ErrorHandler.ThrowOnFailure(this.fileChangeService.IgnoreFile(0, fileName, ignore ? 1 : 0)); + } + } + + /// + /// Stop observing when the file is updated on disk. + /// + /// File to stop observing. + internal void StopObservingItem(string fileName) + { + #region Input validation + if(String.IsNullOrEmpty(fileName)) + { + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "fileName"); + } + #endregion + + string fullFileName = Utilities.CanonicalizeFileName(fileName); + + if(this.observedItems.ContainsKey(fullFileName)) + { + // Get the cookie that was used for this.observedItems to this file. + ObservedItemInfo itemInfo = this.observedItems[fullFileName]; + + // Remove the file from our observed list. It's important that this is done before the call to + // UnadviseFileChange, because for some reason, the call to UnadviseFileChange can trigger a + // FilesChanged event, and we want to be able to filter that event away. + this.observedItems.Remove(fullFileName); + + // Stop observing the file + ErrorHandler.ThrowOnFailure(this.fileChangeService.UnadviseFileChange(itemInfo.FileChangeCookie)); + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/FileDocumentManager.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/FileDocumentManager.cs new file mode 100644 index 0000000000..a101e3d55e --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/FileDocumentManager.cs @@ -0,0 +1,272 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// This class handles opening, saving of file items in the hierarchy. + /// + [CLSCompliant(false)] + public class FileDocumentManager : DocumentManager + { + #region ctors + + public FileDocumentManager(FileNode node) + : base(node) + { + } + #endregion + + #region overriden methods + + /// + /// Open a file using the standard editor + /// + /// In MultiView case determines view to be activated by IVsMultiViewDocumentView. For a list of logical view GUIDS, see constants starting with LOGVIEWID_ defined in NativeMethods class + /// IntPtr to the IUnknown interface of the existing document data object + /// A reference to the window frame that is mapped to the file + /// Determine the UI action on the document window + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public override int Open(ref Guid logicalView, IntPtr docDataExisting, out IVsWindowFrame windowFrame, WindowFrameShowAction windowFrameAction) + { + bool newFile = false; + bool openWith = false; + return this.Open(newFile, openWith, ref logicalView, docDataExisting, out windowFrame, windowFrameAction); + } + + /// + /// Open a file with a specific editor + /// + /// Specifies actions to take when opening a specific editor. Possible editor flags are defined in the enumeration Microsoft.VisualStudio.Shell.Interop.__VSOSPEFLAGS + /// Unique identifier of the editor type + /// Name of the physical view. If null, the environment calls MapLogicalView on the editor factory to determine the physical view that corresponds to the logical view. In this case, null does not specify the primary view, but rather indicates that you do not know which view corresponds to the logical view + /// In MultiView case determines view to be activated by IVsMultiViewDocumentView. For a list of logical view GUIDS, see constants starting with LOGVIEWID_ defined in NativeMethods class + /// IntPtr to the IUnknown interface of the existing document data object + /// A reference to the window frame that is mapped to the file + /// Determine the UI action on the document window + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public override int OpenWithSpecific(uint editorFlags, ref Guid editorType, string physicalView, ref Guid logicalView, IntPtr docDataExisting, out IVsWindowFrame windowFrame, WindowFrameShowAction windowFrameAction) + { + windowFrame = null; + bool newFile = false; + bool openWith = false; + return this.Open(newFile, openWith, editorFlags, ref editorType, physicalView, ref logicalView, docDataExisting, out windowFrame, windowFrameAction); + } + + #endregion + + #region public methods + /// + /// Open a file in a document window with a std editor + /// + /// Open the file as a new file + /// Use a dialog box to determine which editor to use + /// Determine the UI action on the document window + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public int Open(bool newFile, bool openWith, WindowFrameShowAction windowFrameAction) + { + Guid logicalView = Guid.Empty; + IVsWindowFrame windowFrame = null; + return this.Open(newFile, openWith, logicalView, out windowFrame, windowFrameAction); + } + + /// + /// Open a file in a document window with a std editor + /// + /// Open the file as a new file + /// Use a dialog box to determine which editor to use + /// In MultiView case determines view to be activated by IVsMultiViewDocumentView. For a list of logical view GUIDS, see constants starting with LOGVIEWID_ defined in NativeMethods class + /// A reference to the window frame that is mapped to the file + /// Determine the UI action on the document window + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public int Open(bool newFile, bool openWith, Guid logicalView, out IVsWindowFrame frame, WindowFrameShowAction windowFrameAction) + { + frame = null; + IVsRunningDocumentTable rdt = this.Node.ProjectMgr.Site.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + Debug.Assert(rdt != null, " Could not get running document table from the services exposed by this project"); + if(rdt == null) + { + return VSConstants.E_FAIL; + } + + // First we see if someone else has opened the requested view of the file. + _VSRDTFLAGS flags = _VSRDTFLAGS.RDT_NoLock; + uint itemid; + IntPtr docData = IntPtr.Zero; + IVsHierarchy ivsHierarchy; + uint docCookie; + string path = this.GetFullPathForDocument(); + int returnValue = VSConstants.S_OK; + + try + { + ErrorHandler.ThrowOnFailure(rdt.FindAndLockDocument((uint)flags, path, out ivsHierarchy, out itemid, out docData, out docCookie)); + ErrorHandler.ThrowOnFailure(this.Open(newFile, openWith, ref logicalView, docData, out frame, windowFrameAction)); + } + catch(COMException e) + { + Trace.WriteLine("Exception :" + e.Message); + returnValue = e.ErrorCode; + } + finally + { + if(docData != IntPtr.Zero) + { + Marshal.Release(docData); + } + } + + return returnValue; + } + + #endregion + + #region virtual methods + /// + /// Open a file in a document window + /// + /// Open the file as a new file + /// Use a dialog box to determine which editor to use + /// In MultiView case determines view to be activated by IVsMultiViewDocumentView. For a list of logical view GUIDS, see constants starting with LOGVIEWID_ defined in NativeMethods class + /// IntPtr to the IUnknown interface of the existing document data object + /// A reference to the window frame that is mapped to the file + /// Determine the UI action on the document window + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int Open(bool newFile, bool openWith, ref Guid logicalView, IntPtr docDataExisting, out IVsWindowFrame windowFrame, WindowFrameShowAction windowFrameAction) + { + windowFrame = null; + Guid editorType = Guid.Empty; + return this.Open(newFile, openWith, 0, ref editorType, null, ref logicalView, docDataExisting, out windowFrame, windowFrameAction); + } + + #endregion + + #region helper methods + + private int Open(bool newFile, bool openWith, uint editorFlags, ref Guid editorType, string physicalView, ref Guid logicalView, IntPtr docDataExisting, out IVsWindowFrame windowFrame, WindowFrameShowAction windowFrameAction) + { + windowFrame = null; + if(this.Node == null || this.Node.ProjectMgr == null || this.Node.ProjectMgr.IsClosed) + { + return VSConstants.E_FAIL; + } + + Debug.Assert(this.Node != null, "No node has been initialized for the document manager"); + Debug.Assert(this.Node.ProjectMgr != null, "No project manager has been initialized for the document manager"); + Debug.Assert(this.Node is FileNode, "Node is not FileNode object"); + + int returnValue = VSConstants.S_OK; + string caption = this.GetOwnerCaption(); + string fullPath = this.GetFullPathForDocument(); + + // Make sure that the file is on disk before we open the editor and display message if not found + if(!((FileNode)this.Node).IsFileOnDisk(true)) + { + // Inform clients that we have an invalid item (wrong icon) + this.Node.OnInvalidateItems(this.Node.Parent); + + // Bail since we are not able to open the item + // Do not return an error code otherwise an internal error message is shown. The scenario for this operation + // normally is already a reaction to a dialog box telling that the item has been removed. + return VSConstants.S_FALSE; + } + + IVsUIShellOpenDocument uiShellOpenDocument = this.Node.ProjectMgr.Site.GetService(typeof(SVsUIShellOpenDocument)) as IVsUIShellOpenDocument; + IOleServiceProvider serviceProvider = this.Node.ProjectMgr.Site.GetService(typeof(IOleServiceProvider)) as IOleServiceProvider; + + try + { + this.Node.ProjectMgr.OnOpenItem(fullPath); + int result = VSConstants.E_FAIL; + + if(openWith) + { + result = uiShellOpenDocument.OpenStandardEditor((uint)__VSOSEFLAGS.OSE_UseOpenWithDialog, fullPath, ref logicalView, caption, this.Node.ProjectMgr, this.Node.ID, docDataExisting, serviceProvider, out windowFrame); + } + else + { + __VSOSEFLAGS openFlags = 0; + if(newFile) + { + openFlags |= __VSOSEFLAGS.OSE_OpenAsNewFile; + } + + //NOTE: we MUST pass the IVsProject in pVsUIHierarchy and the itemid + // of the node being opened, otherwise the debugger doesn't work. + if(editorType != Guid.Empty) + { + result = uiShellOpenDocument.OpenSpecificEditor(editorFlags, fullPath, ref editorType, physicalView, ref logicalView, caption, this.Node.ProjectMgr, this.Node.ID, docDataExisting, serviceProvider, out windowFrame); + } + else + { + openFlags |= __VSOSEFLAGS.OSE_ChooseBestStdEditor; + result = uiShellOpenDocument.OpenStandardEditor((uint)openFlags, fullPath, ref logicalView, caption, this.Node.ProjectMgr, this.Node.ID, docDataExisting, serviceProvider, out windowFrame); + } + } + + if(result != VSConstants.S_OK && result != VSConstants.S_FALSE && result != VSConstants.OLE_E_PROMPTSAVECANCELLED) + { + ErrorHandler.ThrowOnFailure(result); + } + + if(windowFrame != null) + { + object var; + + if(newFile) + { + ErrorHandler.ThrowOnFailure(windowFrame.GetProperty((int)__VSFPROPID.VSFPROPID_DocData, out var)); + IVsPersistDocData persistDocData = (IVsPersistDocData)var; + ErrorHandler.ThrowOnFailure(persistDocData.SetUntitledDocPath(fullPath)); + } + + var = null; + ErrorHandler.ThrowOnFailure(windowFrame.GetProperty((int)__VSFPROPID.VSFPROPID_DocCookie, out var)); + this.Node.DocCookie = (uint)(int)var; + + if(windowFrameAction == WindowFrameShowAction.Show) + { + ErrorHandler.ThrowOnFailure(windowFrame.Show()); + } + else if(windowFrameAction == WindowFrameShowAction.ShowNoActivate) + { + ErrorHandler.ThrowOnFailure(windowFrame.ShowNoActivate()); + } + else if(windowFrameAction == WindowFrameShowAction.Hide) + { + ErrorHandler.ThrowOnFailure(windowFrame.Hide()); + } + } + } + catch(COMException e) + { + Trace.WriteLine("Exception e:" + e.Message); + returnValue = e.ErrorCode; + CloseWindowFrame(ref windowFrame); + } + + return returnValue; + } + + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/FileNode.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/FileNode.cs new file mode 100644 index 0000000000..d8f6354b4d --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/FileNode.cs @@ -0,0 +1,1079 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants; +using VsCommands = Microsoft.VisualStudio.VSConstants.VSStd97CmdID; +using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID; + +namespace Microsoft.VisualStudio.Project +{ + [CLSCompliant(false)] + [ComVisible(true)] + public class FileNode : HierarchyNode + { + #region static fiels + private static Dictionary extensionIcons; + #endregion + + #region overriden Properties + /// + /// overwrites of the generic hierarchyitem. + /// + [System.ComponentModel.BrowsableAttribute(false)] + public override string Caption + { + get + { + // Use LinkedIntoProjectAt property if available + string caption = this.ItemNode.GetMetadata(ProjectFileConstants.LinkedIntoProjectAt); + if(caption == null || caption.Length == 0) + { + // Otherwise use filename + caption = this.ItemNode.GetMetadata(ProjectFileConstants.Include); + caption = Path.GetFileName(caption); + } + return caption; + } + } + public override int ImageIndex + { + get + { + // Check if the file is there. + if(!this.CanShowDefaultIcon()) + { + return (int)ProjectNode.ImageName.MissingFile; + } + + //Check for known extensions + int imageIndex; + string extension = System.IO.Path.GetExtension(this.FileName); + if((string.IsNullOrEmpty(extension)) || (!extensionIcons.TryGetValue(extension, out imageIndex))) + { + // Missing or unknown extension; let the base class handle this case. + return base.ImageIndex; + } + + // The file type is known and there is an image for it in the image list. + return imageIndex; + } + } + + public override Guid ItemTypeGuid + { + get { return VSConstants.GUID_ItemType_PhysicalFile; } + } + + public override int MenuCommandId + { + get { return VsMenus.IDM_VS_CTXT_ITEMNODE; } + } + + public override string Url + { + get + { + string path = this.ItemNode.GetMetadata(ProjectFileConstants.Include); + if(String.IsNullOrEmpty(path)) + { + return String.Empty; + } + + Url url; + if(Path.IsPathRooted(path)) + { + // Use absolute path + url = new Microsoft.VisualStudio.Shell.Url(path); + } + else + { + // Path is relative, so make it relative to project path + url = new Url(this.ProjectMgr.BaseURI, path); + } + return url.AbsoluteUrl; + + } + } + #endregion + + #region ctor + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + static FileNode() + { + // Build the dictionary with the mapping between some well known extensions + // and the index of the icons inside the standard image list. + extensionIcons = new Dictionary(StringComparer.OrdinalIgnoreCase); + extensionIcons.Add(".aspx", (int)ProjectNode.ImageName.WebForm); + extensionIcons.Add(".asax", (int)ProjectNode.ImageName.GlobalApplicationClass); + extensionIcons.Add(".asmx", (int)ProjectNode.ImageName.WebService); + extensionIcons.Add(".ascx", (int)ProjectNode.ImageName.WebUserControl); + extensionIcons.Add(".asp", (int)ProjectNode.ImageName.ASPPage); + extensionIcons.Add(".config", (int)ProjectNode.ImageName.WebConfig); + extensionIcons.Add(".htm", (int)ProjectNode.ImageName.HTMLPage); + extensionIcons.Add(".html", (int)ProjectNode.ImageName.HTMLPage); + extensionIcons.Add(".css", (int)ProjectNode.ImageName.StyleSheet); + extensionIcons.Add(".xsl", (int)ProjectNode.ImageName.StyleSheet); + extensionIcons.Add(".vbs", (int)ProjectNode.ImageName.ScriptFile); + extensionIcons.Add(".js", (int)ProjectNode.ImageName.ScriptFile); + extensionIcons.Add(".wsf", (int)ProjectNode.ImageName.ScriptFile); + extensionIcons.Add(".txt", (int)ProjectNode.ImageName.TextFile); + extensionIcons.Add(".resx", (int)ProjectNode.ImageName.Resources); + extensionIcons.Add(".rc", (int)ProjectNode.ImageName.Resources); + extensionIcons.Add(".bmp", (int)ProjectNode.ImageName.Bitmap); + extensionIcons.Add(".ico", (int)ProjectNode.ImageName.Icon); + extensionIcons.Add(".gif", (int)ProjectNode.ImageName.Image); + extensionIcons.Add(".jpg", (int)ProjectNode.ImageName.Image); + extensionIcons.Add(".png", (int)ProjectNode.ImageName.Image); + extensionIcons.Add(".map", (int)ProjectNode.ImageName.ImageMap); + extensionIcons.Add(".wav", (int)ProjectNode.ImageName.Audio); + extensionIcons.Add(".mid", (int)ProjectNode.ImageName.Audio); + extensionIcons.Add(".midi", (int)ProjectNode.ImageName.Audio); + extensionIcons.Add(".avi", (int)ProjectNode.ImageName.Video); + extensionIcons.Add(".mov", (int)ProjectNode.ImageName.Video); + extensionIcons.Add(".mpg", (int)ProjectNode.ImageName.Video); + extensionIcons.Add(".mpeg", (int)ProjectNode.ImageName.Video); + extensionIcons.Add(".cab", (int)ProjectNode.ImageName.CAB); + extensionIcons.Add(".jar", (int)ProjectNode.ImageName.JAR); + extensionIcons.Add(".xslt", (int)ProjectNode.ImageName.XSLTFile); + extensionIcons.Add(".xsd", (int)ProjectNode.ImageName.XMLSchema); + extensionIcons.Add(".xml", (int)ProjectNode.ImageName.XMLFile); + extensionIcons.Add(".pfx", (int)ProjectNode.ImageName.PFX); + extensionIcons.Add(".snk", (int)ProjectNode.ImageName.SNK); + } + + /// + /// Constructor for the FileNode + /// + /// Root of the hierarchy + /// Associated project element + public FileNode(ProjectNode root, ProjectElement element) + : base(root, element) + { + if(this.ProjectMgr.NodeHasDesigner(this.ItemNode.GetMetadata(ProjectFileConstants.Include))) + { + this.HasDesigner = true; + } + } + #endregion + + #region overridden methods + protected override NodeProperties CreatePropertiesObject() + { + ISingleFileGenerator generator = this.CreateSingleFileGenerator(); + + return generator == null ? new FileNodeProperties(this) : new SingleFileGeneratorNodeProperties(this); + } + + public override object GetIconHandle(bool open) + { + int index = this.ImageIndex; + if(NoImage == index) + { + // There is no image for this file; let the base class handle this case. + return base.GetIconHandle(open); + } + // Return the handle for the image. + return this.ProjectMgr.ImageHandler.GetIconHandle(index); + } + + /// + /// Get an instance of the automation object for a FileNode + /// + /// An instance of the Automation.OAFileNode if succeeded + public override object GetAutomationObject() + { + if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return null; + } + + return new Automation.OAFileItem(this.ProjectMgr.GetAutomationObject() as Automation.OAProject, this); + } + + /// + /// Renames a file node. + /// + /// The new name. + /// An errorcode for failure or S_OK. + /// + /// + /// We are going to throw instaed of showing messageboxes, since this method is called from various places where a dialog box does not make sense. + /// For example the FileNodeProperties are also calling this method. That should not show directly a messagebox. + /// Also the automation methods are also calling SetEditLabel + /// + + public override int SetEditLabel(string label) + { + // IMPORTANT NOTE: This code will be called when a parent folder is renamed. As such, it is + // expected that we can be called with a label which is the same as the current + // label and this should not be considered a NO-OP. + + if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return VSConstants.E_FAIL; + } + + // Validate the filename. + if(String.IsNullOrEmpty(label)) + { + throw new InvalidOperationException(SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture)); + } + else if(label.Length > NativeMethods.MAX_PATH) + { + throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.PathTooLong, CultureInfo.CurrentUICulture), label)); + } + else if(Utilities.IsFileNameInvalid(label)) + { + throw new InvalidOperationException(SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture)); + } + + for(HierarchyNode n = this.Parent.FirstChild; n != null; n = n.NextSibling) + { + if(n != this && String.Compare(n.Caption, label, StringComparison.OrdinalIgnoreCase) == 0) + { + //A file or folder with the name '{0}' already exists on disk at this location. Please choose another name. + //If this file or folder does not appear in the Solution Explorer, then it is not currently part of your project. To view files which exist on disk, but are not in the project, select Show All Files from the Project menu. + throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.FileOrFolderAlreadyExists, CultureInfo.CurrentUICulture), label)); + } + } + + string fileName = Path.GetFileNameWithoutExtension(label); + + // If there is no filename or it starts with a leading dot issue an error message and quit. + if(String.IsNullOrEmpty(fileName) || fileName[0] == '.') + { + throw new InvalidOperationException(SR.GetString(SR.FileNameCannotContainALeadingPeriod, CultureInfo.CurrentUICulture)); + } + + // Verify that the file extension is unchanged + string strRelPath = Path.GetFileName(this.ItemNode.GetMetadata(ProjectFileConstants.Include)); + if(String.Compare(Path.GetExtension(strRelPath), Path.GetExtension(label), StringComparison.OrdinalIgnoreCase) != 0) + { + // Prompt to confirm that they really want to change the extension of the file + string message = SR.GetString(SR.ConfirmExtensionChange, CultureInfo.CurrentUICulture, new string[] { label }); + IVsUIShell shell = this.ProjectMgr.Site.GetService(typeof(SVsUIShell)) as IVsUIShell; + + Debug.Assert(shell != null, "Could not get the ui shell from the project"); + if(shell == null) + { + return VSConstants.E_FAIL; + } + + if(!VsShellUtilities.PromptYesNo(message, null, OLEMSGICON.OLEMSGICON_INFO, shell)) + { + // The user cancelled the confirmation for changing the extension. + // Return S_OK in order not to show any extra dialog box + return VSConstants.S_OK; + } + } + + + // Build the relative path by looking at folder names above us as one scenarios + // where we get called is when a folder above us gets renamed (in which case our path is invalid) + HierarchyNode parent = this.Parent; + while(parent != null && (parent is FolderNode)) + { + strRelPath = Path.Combine(parent.Caption, strRelPath); + parent = parent.Parent; + } + + return SetEditLabel(label, strRelPath); + } + + public override string GetMkDocument() + { + Debug.Assert(this.Url != null, "No url sepcified for this node"); + + return this.Url; + } + + /// + /// Delete the item corresponding to the specified path from storage. + /// + /// + protected internal override void DeleteFromStorage(string path) + { + if(File.Exists(path)) + { + File.SetAttributes(path, FileAttributes.Normal); // make sure it's not readonly. + File.Delete(path); + } + } + + /// + /// Rename the underlying document based on the change the user just made to the edit label. + /// + protected internal override int SetEditLabel(string label, string relativePath) + { + int returnValue = VSConstants.S_OK; + uint oldId = this.ID; + string strSavePath = Path.GetDirectoryName(relativePath); + + if(!Path.IsPathRooted(relativePath)) + { + strSavePath = Path.Combine(Path.GetDirectoryName(this.ProjectMgr.BaseURI.Uri.LocalPath), strSavePath); + } + + string newName = Path.Combine(strSavePath, label); + + if(NativeMethods.IsSamePath(newName, this.Url)) + { + // If this is really a no-op, then nothing to do + if(String.Compare(newName, this.Url, StringComparison.Ordinal) == 0) + return VSConstants.S_FALSE; + } + else + { + // If the renamed file already exists then quit (unless it is the result of the parent having done the move). + if(IsFileOnDisk(newName) + && (IsFileOnDisk(this.Url) + || String.Compare(Path.GetFileName(newName), Path.GetFileName(this.Url), StringComparison.Ordinal) != 0)) + { + throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.FileCannotBeRenamedToAnExistingFile, CultureInfo.CurrentUICulture), label)); + } + else if(newName.Length > NativeMethods.MAX_PATH) + { + throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.PathTooLong, CultureInfo.CurrentUICulture), label)); + } + + } + + string oldName = this.Url; + // must update the caption prior to calling RenameDocument, since it may + // cause queries of that property (such as from open editors). + string oldrelPath = this.ItemNode.GetMetadata(ProjectFileConstants.Include); + + try + { + if(!RenameDocument(oldName, newName)) + { + this.ItemNode.Rename(oldrelPath); + this.ItemNode.RefreshProperties(); + } + + if(this is DependentFileNode) + { + OnInvalidateItems(this.Parent); + } + + } + catch(Exception e) + { + // Just re-throw the exception so we don't get duplicate message boxes. + Trace.WriteLine("Exception : " + e.Message); + this.RecoverFromRenameFailure(newName, oldrelPath); + returnValue = Marshal.GetHRForException(e); + throw; + } + // Return S_FALSE if the hierarchy item id has changed. This forces VS to flush the stale + // hierarchy item id. + if(returnValue == (int)VSConstants.S_OK || returnValue == (int)VSConstants.S_FALSE || returnValue == VSConstants.OLE_E_PROMPTSAVECANCELLED) + { + return (oldId == this.ID) ? VSConstants.S_OK : (int)VSConstants.S_FALSE; + } + + return returnValue; + } + + /// + /// Returns a specific Document manager to handle files + /// + /// Document manager object + protected internal override DocumentManager GetDocumentManager() + { + return new FileDocumentManager(this); + } + + /// + /// Called by the drag&drop implementation to ask the node + /// which is being dragged/droped over which nodes should + /// process the operation. + /// This allows for dragging to a node that cannot contain + /// items to let its parent accept the drop, while a reference + /// node delegate to the project and a folder/project node to itself. + /// + /// + protected internal override HierarchyNode GetDragTargetHandlerNode() + { + Debug.Assert(this.ProjectMgr != null, " The project manager is null for the filenode"); + HierarchyNode handlerNode = this; + while(handlerNode != null && !(handlerNode is ProjectNode || handlerNode is FolderNode)) + handlerNode = handlerNode.Parent; + if(handlerNode == null) + handlerNode = this.ProjectMgr; + return handlerNode; + } + + protected override int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) + { + if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + // Exec on special filenode commands + if(cmdGroup == VsMenus.guidStandardCommandSet97) + { + IVsWindowFrame windowFrame = null; + + switch((VsCommands)cmd) + { + case VsCommands.ViewCode: + return ((FileDocumentManager)this.GetDocumentManager()).Open(false, false, VSConstants.LOGVIEWID_Code, out windowFrame, WindowFrameShowAction.Show); + + case VsCommands.ViewForm: + return ((FileDocumentManager)this.GetDocumentManager()).Open(false, false, VSConstants.LOGVIEWID_Designer, out windowFrame, WindowFrameShowAction.Show); + + case VsCommands.Open: + return ((FileDocumentManager)this.GetDocumentManager()).Open(false, false, WindowFrameShowAction.Show); + + case VsCommands.OpenWith: + return ((FileDocumentManager)this.GetDocumentManager()).Open(false, true, VSConstants.LOGVIEWID_UserChooseView, out windowFrame, WindowFrameShowAction.Show); + } + } + + // Exec on special filenode commands + if(cmdGroup == VsMenus.guidStandardCommandSet2K) + { + switch((VsCommands2K)cmd) + { + case VsCommands2K.RUNCUSTOMTOOL: + { + try + { + this.RunGenerator(); + return VSConstants.S_OK; + } + catch(Exception e) + { + Trace.WriteLine("Running Custom Tool failed : " + e.Message); + throw; + } + } + } + } + + return base.ExecCommandOnNode(cmdGroup, cmd, nCmdexecopt, pvaIn, pvaOut); + } + + + protected override int QueryStatusOnNode(Guid cmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result) + { + if(cmdGroup == VsMenus.guidStandardCommandSet97) + { + switch((VsCommands)cmd) + { + case VsCommands.Copy: + case VsCommands.Paste: + case VsCommands.Cut: + case VsCommands.Rename: + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + + case VsCommands.ViewCode: + //case VsCommands.Delete: goto case VsCommands.OpenWith; + case VsCommands.Open: + case VsCommands.OpenWith: + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + } + } + else if(cmdGroup == VsMenus.guidStandardCommandSet2K) + { + if((VsCommands2K)cmd == VsCommands2K.EXCLUDEFROMPROJECT) + { + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + } + if((VsCommands2K)cmd == VsCommands2K.RUNCUSTOMTOOL) + { + if(string.IsNullOrEmpty(this.ItemNode.GetMetadata(ProjectFileConstants.DependentUpon)) && (this.NodeProperties is SingleFileGeneratorNodeProperties)) + { + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + } + } + } + else + { + return (int)OleConstants.OLECMDERR_E_UNKNOWNGROUP; + } + return base.QueryStatusOnNode(cmdGroup, cmd, pCmdText, ref result); + } + + + protected override void DoDefaultAction() + { + CCITracing.TraceCall(); + FileDocumentManager manager = this.GetDocumentManager() as FileDocumentManager; + Debug.Assert(manager != null, "Could not get the FileDocumentManager"); + manager.Open(false, false, WindowFrameShowAction.Show); + } + + /// + /// Performs a SaveAs operation of an open document. Called from SaveItem after the running document table has been updated with the new doc data. + /// + /// A pointer to the document in the rdt + /// The new file path to the document + /// + protected override int AfterSaveItemAs(IntPtr docData, string newFilePath) + { + if(String.IsNullOrEmpty(newFilePath)) + { + throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "newFilePath"); + } + + int returnCode = VSConstants.S_OK; + newFilePath = newFilePath.Trim(); + + //Identify if Path or FileName are the same for old and new file + string newDirectoryName = Path.GetDirectoryName(newFilePath); + Uri newDirectoryUri = new Uri(newDirectoryName); + string newCanonicalDirectoryName = newDirectoryUri.LocalPath; + newCanonicalDirectoryName = newCanonicalDirectoryName.TrimEnd(Path.DirectorySeparatorChar); + string oldCanonicalDirectoryName = new Uri(Path.GetDirectoryName(this.GetMkDocument())).LocalPath; + oldCanonicalDirectoryName = oldCanonicalDirectoryName.TrimEnd(Path.DirectorySeparatorChar); + string errorMessage = String.Empty; + bool isSamePath = NativeMethods.IsSamePath(newCanonicalDirectoryName, oldCanonicalDirectoryName); + bool isSameFile = NativeMethods.IsSamePath(newFilePath, this.Url); + + // Currently we do not support if the new directory is located outside the project cone + string projectCannonicalDirecoryName = new Uri(this.ProjectMgr.ProjectFolder).LocalPath; + projectCannonicalDirecoryName = projectCannonicalDirecoryName.TrimEnd(Path.DirectorySeparatorChar); + if(!isSamePath && newCanonicalDirectoryName.IndexOf(projectCannonicalDirecoryName, StringComparison.OrdinalIgnoreCase) == -1) + { + errorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.LinkedItemsAreNotSupported, CultureInfo.CurrentUICulture), Path.GetFileNameWithoutExtension(newFilePath)); + throw new InvalidOperationException(errorMessage); + } + + //Get target container + HierarchyNode targetContainer = null; + if(isSamePath) + { + targetContainer = this.Parent; + } + else if(NativeMethods.IsSamePath(newCanonicalDirectoryName, projectCannonicalDirecoryName)) + { + //the projectnode is the target container + targetContainer = this.ProjectMgr; + } + else + { + //search for the target container among existing child nodes + targetContainer = this.ProjectMgr.FindChild(newDirectoryName); + if(targetContainer != null && (targetContainer is FileNode)) + { + // We already have a file node with this name in the hierarchy. + errorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.FileAlreadyExistsAndCannotBeRenamed, CultureInfo.CurrentUICulture), Path.GetFileNameWithoutExtension(newFilePath)); + throw new InvalidOperationException(errorMessage); + } + } + + if(targetContainer == null) + { + // Add a chain of subdirectories to the project. + string relativeUri = PackageUtilities.GetPathDistance(this.ProjectMgr.BaseURI.Uri, newDirectoryUri); + Debug.Assert(!String.IsNullOrEmpty(relativeUri) && relativeUri != newDirectoryUri.LocalPath, "Could not make pat distance of " + this.ProjectMgr.BaseURI.Uri.LocalPath + " and " + newDirectoryUri); + targetContainer = this.ProjectMgr.CreateFolderNodes(relativeUri); + } + Debug.Assert(targetContainer != null, "We should have found a target node by now"); + + //Suspend file changes while we rename the document + string oldrelPath = this.ItemNode.GetMetadata(ProjectFileConstants.Include); + string oldName = Path.Combine(this.ProjectMgr.ProjectFolder, oldrelPath); + SuspendFileChanges sfc = new SuspendFileChanges(this.ProjectMgr.Site, oldName); + sfc.Suspend(); + + try + { + // Rename the node. + DocumentManager.UpdateCaption(this.ProjectMgr.Site, Path.GetFileName(newFilePath), docData); + // Check if the file name was actually changed. + // In same cases (e.g. if the item is a file and the user has changed its encoding) this function + // is called even if there is no real rename. + if(!isSameFile || (this.Parent.ID != targetContainer.ID)) + { + // The path of the file is changed or its parent is changed; in both cases we have + // to rename the item. + this.RenameFileNode(oldName, newFilePath, targetContainer.ID); + OnInvalidateItems(this.Parent); + } + } + catch(Exception e) + { + Trace.WriteLine("Exception : " + e.Message); + this.RecoverFromRenameFailure(newFilePath, oldrelPath); + throw; + } + finally + { + sfc.Resume(); + } + + return returnCode; + } + + /// + /// Determines if this is node a valid node for painting the default file icon. + /// + /// + protected override bool CanShowDefaultIcon() + { + string moniker = this.GetMkDocument(); + + if(String.IsNullOrEmpty(moniker) || !File.Exists(moniker)) + { + return false; + } + + return true; + } + + #endregion + + #region virtual methods + public virtual string FileName + { + get + { + return this.Caption; + } + set + { + this.SetEditLabel(value); + } + } + + /// + /// Determine if this item is represented physical on disk and shows a messagebox in case that the file is not present and a UI is to be presented. + /// + /// true if user should be presented for UI in case the file is not present + /// true if file is on disk + internal protected virtual bool IsFileOnDisk(bool showMessage) + { + bool fileExist = IsFileOnDisk(this.Url); + + if(!fileExist && showMessage && !Utilities.IsInAutomationFunction(this.ProjectMgr.Site)) + { + string message = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.ItemDoesNotExistInProjectDirectory, CultureInfo.CurrentUICulture), this.Caption); + string title = string.Empty; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + VsShellUtilities.ShowMessageBox(this.ProjectMgr.Site, title, message, icon, buttons, defaultButton); + } + + return fileExist; + } + + /// + /// Determine if the file represented by "path" exist in storage. + /// Override this method if your files are not persisted on disk. + /// + /// Url representing the file + /// True if the file exist + internal protected virtual bool IsFileOnDisk(string path) + { + return File.Exists(path); + } + + /// + /// Renames the file in the hierarchy by removing old node and adding a new node in the hierarchy. + /// + /// The old file name. + /// The new file name + /// The new parent id of the item. + /// The newly added FileNode. + /// While a new node will be used to represent the item, the underlying MSBuild item will be the same and as a result file properties saved in the project file will not be lost. + protected virtual FileNode RenameFileNode(string oldFileName, string newFileName, uint newParentId) + { + if(string.Compare(oldFileName, newFileName, StringComparison.Ordinal) == 0) + { + // We do not want to rename the same file + return null; + } + + this.OnItemDeleted(); + this.Parent.RemoveChild(this); + + // Since this node has been removed all of its state is zombied at this point + // Do not call virtual methods after this point since the object is in a deleted state. + + string[] file = new string[1]; + file[0] = newFileName; + VSADDRESULT[] result = new VSADDRESULT[1]; + Guid emptyGuid = Guid.Empty; + ErrorHandler.ThrowOnFailure(this.ProjectMgr.AddItemWithSpecific(newParentId, VSADDITEMOPERATION.VSADDITEMOP_OPENFILE, null, 0, file, IntPtr.Zero, 0, ref emptyGuid, null, ref emptyGuid, result)); + FileNode childAdded = this.ProjectMgr.FindChild(newFileName) as FileNode; + Debug.Assert(childAdded != null, "Could not find the renamed item in the hierarchy"); + // Update the itemid to the newly added. + this.ID = childAdded.ID; + + // Remove the item created by the add item. We need to do this otherwise we will have two items. + // Please be aware that we have not removed the ItemNode associated to the removed file node from the hierrachy. + // What we want to achieve here is to reuse the existing build item. + // We want to link to the newly created node to the existing item node and addd the new include. + + //temporarily keep properties from new itemnode since we are going to overwrite it + string newInclude = childAdded.ItemNode.Item.EvaluatedInclude; + string dependentOf = childAdded.ItemNode.GetMetadata(ProjectFileConstants.DependentUpon); + childAdded.ItemNode.RemoveFromProjectFile(); + + // Assign existing msbuild item to the new childnode + childAdded.ItemNode = this.ItemNode; + childAdded.ItemNode.Item.ItemType = this.ItemNode.ItemName; + childAdded.ItemNode.Item.Xml.Include = newInclude; + if(!string.IsNullOrEmpty(dependentOf)) + childAdded.ItemNode.SetMetadata(ProjectFileConstants.DependentUpon, dependentOf); + childAdded.ItemNode.RefreshProperties(); + + //Update the new document in the RDT. + DocumentManager.RenameDocument(this.ProjectMgr.Site, oldFileName, newFileName, childAdded.ID); + + //Select the new node in the hierarchy + IVsUIHierarchyWindow uiWindow = UIHierarchyUtilities.GetUIHierarchyWindow(this.ProjectMgr.Site, SolutionExplorer); + ErrorHandler.ThrowOnFailure(uiWindow.ExpandItem(this.ProjectMgr, this.ID, EXPANDFLAGS.EXPF_SelectItem)); + + //Update FirstChild + childAdded.FirstChild = this.FirstChild; + + //Update ChildNodes + SetNewParentOnChildNodes(childAdded); + RenameChildNodes(childAdded); + + return childAdded; + } + + /// + /// Rename all childnodes + /// + /// The newly added Parent node. + protected virtual void RenameChildNodes(FileNode parentNode) + { + foreach(HierarchyNode child in GetChildNodes()) + { + FileNode childNode = child as FileNode; + if(null == childNode) + { + continue; + } + string newfilename; + if(childNode.HasParentNodeNameRelation) + { + string relationalName = childNode.Parent.GetRelationalName(); + string extension = childNode.GetRelationNameExtension(); + newfilename = relationalName + extension; + newfilename = Path.Combine(Path.GetDirectoryName(childNode.Parent.GetMkDocument()), newfilename); + } + else + { + newfilename = Path.Combine(Path.GetDirectoryName(childNode.Parent.GetMkDocument()), childNode.Caption); + } + + childNode.RenameDocument(childNode.GetMkDocument(), newfilename); + + //We must update the DependsUpon property since the rename operation will not do it if the childNode is not renamed + //which happens if the is no name relation between the parent and the child + string dependentOf = childNode.ItemNode.GetMetadata(ProjectFileConstants.DependentUpon); + if(!string.IsNullOrEmpty(dependentOf)) + { + childNode.ItemNode.SetMetadata(ProjectFileConstants.DependentUpon, childNode.Parent.ItemNode.GetMetadata(ProjectFileConstants.Include)); + } + } + } + + + /// + /// Tries recovering from a rename failure. + /// + /// The file that failed to be renamed. + /// The original filenamee + protected virtual void RecoverFromRenameFailure(string fileThatFailed, string originalFileName) + { + if(this.ItemNode != null && !String.IsNullOrEmpty(originalFileName)) + { + this.ItemNode.Rename(originalFileName); + } + } + + protected override bool CanDeleteItem(__VSDELETEITEMOPERATION deleteOperation) + { + if(deleteOperation == __VSDELETEITEMOPERATION.DELITEMOP_DeleteFromStorage) + { + return this.ProjectMgr.CanProjectDeleteItems; + } + return false; + } + + /// + /// This should be overriden for node that are not saved on disk + /// + /// Previous name in storage + /// New name in storage + protected virtual void RenameInStorage(string oldName, string newName) + { + File.Move(oldName, newName); + } + + /// + /// factory method for creating single file generators. + /// + /// + protected virtual ISingleFileGenerator CreateSingleFileGenerator() + { + return new SingleFileGenerator(this.ProjectMgr); + } + + /// + /// This method should be overridden to provide the list of special files and associated flags for source control. + /// + /// One of the file associated to the node. + /// The list of files to be placed under source control. + /// The flags that are associated to the files. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "scc")] + protected internal override void GetSccSpecialFiles(string sccFile, IList files, IList flags) + { + if(this.ExcludeNodeFromScc) + { + return; + } + + if(files == null) + { + throw new ArgumentNullException("files"); + } + + if(flags == null) + { + throw new ArgumentNullException("flags"); + } + + foreach(HierarchyNode node in this.GetChildNodes()) + { + files.Add(node.GetMkDocument()); + } + } + + #endregion + + #region Helper methods + /// + /// Get's called to rename the eventually running document this hierarchyitem points to + /// + /// returns FALSE if the doc can not be renamed + internal bool RenameDocument(string oldName, string newName) + { + IVsRunningDocumentTable pRDT = this.GetService(typeof(IVsRunningDocumentTable)) as IVsRunningDocumentTable; + if(pRDT == null) return false; + IntPtr docData = IntPtr.Zero; + IVsHierarchy pIVsHierarchy; + uint itemId; + uint uiVsDocCookie; + + SuspendFileChanges sfc = new SuspendFileChanges(this.ProjectMgr.Site, oldName); + sfc.Suspend(); + + try + { + // Suspend ms build since during a rename operation no msbuild re-evaluation should be performed until we have finished. + // Scenario that could fail if we do not suspend. + // We have a project system relying on MPF that triggers a Compile target build (re-evaluates itself) whenever the project changes. (example: a file is added, property changed.) + // 1. User renames a file in the above project sytem relying on MPF + // 2. Our rename funstionality implemented in this method removes and readds the file and as a post step copies all msbuild entries from the removed file to the added file. + // 3. The project system mentioned will trigger an msbuild re-evaluate with the new item, because it was listening to OnItemAdded. + // The problem is that the item at the "add" time is only partly added to the project, since the msbuild part has not yet been copied over as mentioned in part 2 of the last step of the rename process. + // The result is that the project re-evaluates itself wrongly. + VSRENAMEFILEFLAGS renameflag = VSRENAMEFILEFLAGS.VSRENAMEFILEFLAGS_NoFlags; + try + { + this.ProjectMgr.SuspendMSBuild(); + ErrorHandler.ThrowOnFailure(pRDT.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, oldName, out pIVsHierarchy, out itemId, out docData, out uiVsDocCookie)); + + if(pIVsHierarchy != null && !Utilities.IsSameComObject(pIVsHierarchy, this.ProjectMgr)) + { + // Don't rename it if it wasn't opened by us. + return false; + } + + // ask other potentially running packages + if(!this.ProjectMgr.Tracker.CanRenameItem(oldName, newName, renameflag)) + { + return false; + } + // Allow the user to "fix" the project by renaming the item in the hierarchy + // to the real name of the file on disk. + if(IsFileOnDisk(oldName) || !IsFileOnDisk(newName)) + { + RenameInStorage(oldName, newName); + } + + string newFileName = Path.GetFileName(newName); + + bool caseOnlyChange = NativeMethods.IsSamePath(oldName, newName); + if(!caseOnlyChange) + { + // Check out the project file if necessary. + if(!this.ProjectMgr.QueryEditProjectFile(false)) + { + throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + + this.RenameFileNode(oldName, newName); + } + else + { + this.RenameCaseOnlyChange(newFileName); + } + + DocumentManager.UpdateCaption(this.ProjectMgr.Site, Caption, docData); + } + finally + { + this.ProjectMgr.ResumeMSBuild(this.ProjectMgr.ReEvaluateProjectFileTargetName); + } + + // changed from MPFProj: + // http://mpfproj10.codeplex.com/WorkItem/View.aspx?WorkItemId=8231 + this.ProjectMgr.Tracker.OnItemRenamed(oldName, newName, renameflag); + } + finally + { + sfc.Resume(); + if(docData != IntPtr.Zero) + { + Marshal.Release(docData); + } + } + + return true; + } + + private FileNode RenameFileNode(string oldFileName, string newFileName) + { + return this.RenameFileNode(oldFileName, newFileName, this.Parent.ID); + } + + /// + /// Renames the file node for a case only change. + /// + /// The new file name. + private void RenameCaseOnlyChange(string newFileName) + { + //Update the include for this item. + string include = this.ItemNode.Item.EvaluatedInclude; + if(String.Compare(include, newFileName, StringComparison.OrdinalIgnoreCase) == 0) + { + this.ItemNode.Item.Xml.Include = newFileName; + } + else + { + string includeDir = Path.GetDirectoryName(include); + this.ItemNode.Item.Xml.Include = Path.Combine(includeDir, newFileName); + } + + this.ItemNode.RefreshProperties(); + + this.ReDraw(UIHierarchyElement.Caption); + this.RenameChildNodes(this); + + // Refresh the property browser. + IVsUIShell shell = this.ProjectMgr.Site.GetService(typeof(SVsUIShell)) as IVsUIShell; + Debug.Assert(shell != null, "Could not get the ui shell from the project"); + if(shell == null) + { + throw new InvalidOperationException(); + } + + ErrorHandler.ThrowOnFailure(shell.RefreshPropertyBrowser(0)); + + //Select the new node in the hierarchy + IVsUIHierarchyWindow uiWindow = UIHierarchyUtilities.GetUIHierarchyWindow(this.ProjectMgr.Site, SolutionExplorer); + ErrorHandler.ThrowOnFailure(uiWindow.ExpandItem(this.ProjectMgr, this.ID, EXPANDFLAGS.EXPF_SelectItem)); + } + + #endregion + + #region SingleFileGenerator Support methods + /// + /// Event handler for the Custom tool property changes + /// + /// FileNode sending it + /// Node event args + internal virtual void OnCustomToolChanged(object sender, HierarchyNodeEventArgs e) + { + this.RunGenerator(); + } + + /// + /// Event handler for the Custom tool namespce property changes + /// + /// FileNode sending it + /// Node event args + internal virtual void OnCustomToolNameSpaceChanged(object sender, HierarchyNodeEventArgs e) + { + this.RunGenerator(); + } + + #endregion + + #region helpers + /// + /// Runs a generator. + /// + internal void RunGenerator() + { + ISingleFileGenerator generator = this.CreateSingleFileGenerator(); + if(generator != null) + { + generator.RunGenerator(this.Url); + } + } + + /// + /// Update the ChildNodes after the parent node has been renamed + /// + /// The new FileNode created as part of the rename of this node + private void SetNewParentOnChildNodes(FileNode newFileNode) + { + foreach(HierarchyNode childNode in GetChildNodes()) + { + childNode.Parent = newFileNode; + } + } + + private List GetChildNodes() + { + List childNodes = new List(); + HierarchyNode childNode = this.FirstChild; + while(childNode != null) + { + childNodes.Add(childNode); + childNode = childNode.NextSibling; + } + return childNodes; + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/FolderNode.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/FolderNode.cs new file mode 100644 index 0000000000..5deca75a16 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/FolderNode.cs @@ -0,0 +1,459 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants; +using VsCommands = Microsoft.VisualStudio.VSConstants.VSStd97CmdID; +using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID; + +namespace Microsoft.VisualStudio.Project +{ + [CLSCompliant(false)] + [ComVisible(true)] + public class FolderNode : HierarchyNode + { + #region ctors + /// + /// Constructor for the FolderNode + /// + /// Root node of the hierarchy + /// relative path from root i.e.: "NewFolder1\\NewFolder2\\NewFolder3 + /// Associated project element + public FolderNode(ProjectNode root, string relativePath, ProjectElement element) + : base(root, element) + { + if (relativePath == null) + { + throw new ArgumentNullException("relativePath"); + } + + this.VirtualNodeName = relativePath.TrimEnd('\\'); + } + #endregion + + #region overridden properties + public override int SortPriority + { + get { return DefaultSortOrderNode.FolderNode; } + } + + /// + /// This relates to the SCC glyph + /// + public override VsStateIcon StateIconIndex + { + get + { + // The SCC manager does not support being asked for the state icon of a folder (result of the operation is undefined) + return VsStateIcon.STATEICON_NOSTATEICON; + } + } + #endregion + + #region overridden methods + protected override NodeProperties CreatePropertiesObject() + { + return new FolderNodeProperties(this); + } + + protected internal override void DeleteFromStorage(string path) + { + this.DeleteFolder(path); + } + + /// + /// Get the automation object for the FolderNode + /// + /// An instance of the Automation.OAFolderNode type if succeeded + public override object GetAutomationObject() + { + if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return null; + } + + return new Automation.OAFolderItem(this.ProjectMgr.GetAutomationObject() as Automation.OAProject, this); + } + + public override object GetIconHandle(bool open) + { + return this.ProjectMgr.ImageHandler.GetIconHandle(open ? (int)ProjectNode.ImageName.OpenFolder : (int)ProjectNode.ImageName.Folder); + } + + /// + /// Rename Folder + /// + /// new Name of Folder + /// VSConstants.S_OK, if succeeded + public override int SetEditLabel(string label) + { + if(String.Compare(Path.GetFileName(this.Url.TrimEnd('\\')), label, StringComparison.Ordinal) == 0) + { + // Label matches current Name + return VSConstants.S_OK; + } + + string newPath = Path.Combine(new DirectoryInfo(this.Url).Parent.FullName, label); + + // Verify that No Directory/file already exists with the new name among current children + for(HierarchyNode n = Parent.FirstChild; n != null; n = n.NextSibling) + { + if(n != this && String.Compare(n.Caption, label, StringComparison.OrdinalIgnoreCase) == 0) + { + return ShowFileOrFolderAlreadExistsErrorMessage(newPath); + } + } + + // Verify that No Directory/file already exists with the new name on disk + if(Directory.Exists(newPath) || File.Exists(newPath)) + { + return ShowFileOrFolderAlreadExistsErrorMessage(newPath); + } + + try + { + RenameFolder(label); + + //Refresh the properties in the properties window + IVsUIShell shell = this.ProjectMgr.GetService(typeof(SVsUIShell)) as IVsUIShell; + Debug.Assert(shell != null, "Could not get the ui shell from the project"); + ErrorHandler.ThrowOnFailure(shell.RefreshPropertyBrowser(0)); + + // Notify the listeners that the name of this folder is changed. This will + // also force a refresh of the SolutionExplorer's node. + this.OnPropertyChanged(this, (int)__VSHPROPID.VSHPROPID_Caption, 0); + } + catch(Exception e) + { + throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.RenameFolder, CultureInfo.CurrentUICulture), e.Message)); + } + return VSConstants.S_OK; + } + + + public override int MenuCommandId + { + get { return VsMenus.IDM_VS_CTXT_FOLDERNODE; } + } + + public override Guid ItemTypeGuid + { + get + { + return VSConstants.GUID_ItemType_PhysicalFolder; + } + } + + public override string Url + { + get + { + return Path.Combine(Path.GetDirectoryName(this.ProjectMgr.Url), this.VirtualNodeName) + "\\"; + } + } + + public override string Caption + { + get + { + // it might have a backslash at the end... + // and it might consist of Grandparent\parent\this\ + string caption = this.VirtualNodeName; + string[] parts; + parts = caption.Split(Path.DirectorySeparatorChar); + caption = parts[parts.GetUpperBound(0)]; + return caption; + } + } + + public override string GetMkDocument() + { + Debug.Assert(this.Url != null, "No url sepcified for this node"); + + return this.Url; + } + + /// + /// Enumerate the files associated with this node. + /// A folder node is not a file and as such no file to enumerate. + /// + /// The list of files to be placed under source control. + /// The flags that are associated to the files. + protected internal override void GetSccFiles(System.Collections.Generic.IList files, System.Collections.Generic.IList flags) + { + return; + } + + /// + /// This method should be overridden to provide the list of special files and associated flags for source control. + /// + /// One of the file associated to the node. + /// The list of files to be placed under source control. + /// The flags that are associated to the files. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "scc")] + protected internal override void GetSccSpecialFiles(string sccFile, IList files, IList flags) + { + if(this.ExcludeNodeFromScc) + { + return; + } + + if(files == null) + { + throw new ArgumentNullException("files"); + } + + if(flags == null) + { + throw new ArgumentNullException("flags"); + } + + if(string.IsNullOrEmpty(sccFile)) + { + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "sccFile"); + } + + // Get the file node for the file passed in. + FileNode node = this.FindChild(sccFile) as FileNode; + + // Dependents do not participate directly in scc. + if(node != null && !(node is DependentFileNode)) + { + node.GetSccSpecialFiles(sccFile, files, flags); + } + } + + /// + /// Recursevily walks the folder nodes and redraws the state icons + /// + protected internal override void UpdateSccStateIcons() + { + for(HierarchyNode child = this.FirstChild; child != null; child = child.NextSibling) + { + child.UpdateSccStateIcons(); + } + } + + protected override int QueryStatusOnNode(Guid cmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result) + { + if(cmdGroup == VsMenus.guidStandardCommandSet97) + { + switch((VsCommands)cmd) + { + case VsCommands.Copy: + case VsCommands.Paste: + case VsCommands.Cut: + case VsCommands.Rename: + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + + case VsCommands.NewFolder: + case VsCommands.AddNewItem: + case VsCommands.AddExistingItem: + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + } + } + else if(cmdGroup == VsMenus.guidStandardCommandSet2K) + { + if((VsCommands2K)cmd == VsCommands2K.EXCLUDEFROMPROJECT) + { + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + } + } + else + { + return (int)OleConstants.OLECMDERR_E_UNKNOWNGROUP; + } + return base.QueryStatusOnNode(cmdGroup, cmd, pCmdText, ref result); + } + + protected override bool CanDeleteItem(__VSDELETEITEMOPERATION deleteOperation) + { + if(deleteOperation == __VSDELETEITEMOPERATION.DELITEMOP_DeleteFromStorage) + { + return this.ProjectMgr.CanProjectDeleteItems; + } + return false; + } + + #endregion + + #region virtual methods + /// + /// Override if your node is not a file system folder so that + /// it does nothing or it deletes it from your storage location. + /// + /// Path to the folder to delete + public virtual void DeleteFolder(string path) + { + if(Directory.Exists(path)) + Directory.Delete(path, true); + } + + /// + /// creates the physical directory for a folder node + /// Override if your node does not use file system folder + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "e")] + public virtual void CreateDirectory() + { + try + { + if(Directory.Exists(this.Url) == false) + { + Directory.CreateDirectory(this.Url); + } + } + //TODO - this should not digest all exceptions. + catch(System.Exception e) + { + CCITracing.Trace(e); + throw; + } + } + /// + /// Creates a folder nodes physical directory + /// Override if your node does not use file system folder + /// + /// + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "e")] + public virtual void CreateDirectory(string newName) + { + if(String.IsNullOrEmpty(newName)) + { + throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "newName"); + } + + try + { + // on a new dir && enter, we get called with the same name (so do nothing if name is the same + char[] dummy = new char[1]; + dummy[0] = Path.DirectorySeparatorChar; + string oldDir = this.Url; + oldDir = oldDir.TrimEnd(dummy); + string strNewDir = Path.Combine(Path.GetDirectoryName(oldDir), newName); + + if(String.Compare(strNewDir, oldDir, StringComparison.OrdinalIgnoreCase) != 0) + { + if(Directory.Exists(strNewDir)) + { + throw new InvalidOperationException(SR.GetString(SR.DirectoryExistError, CultureInfo.CurrentUICulture)); + } + Directory.CreateDirectory(strNewDir); + } + } + //TODO - this should not digest all exceptions. + catch(System.Exception e) + { + CCITracing.Trace(e); + throw; + } + } + + /// + /// Rename the physical directory for a folder node + /// Override if your node does not use file system folder + /// + /// + public virtual void RenameDirectory(string newPath) + { + if(Directory.Exists(this.Url)) + { + if(Directory.Exists(newPath)) + { + ShowFileOrFolderAlreadExistsErrorMessage(newPath); + } + + Directory.Move(this.Url, newPath); + } + } + #endregion + + #region helper methods + + // Made public for IronStudio directory based projects: + public void RenameFolder(string newName) + { + // Do the rename (note that we only do the physical rename if the leaf name changed) + string newPath = Path.Combine(this.Parent.VirtualNodeName, newName); + if(String.Compare(Path.GetFileName(VirtualNodeName), newName, StringComparison.Ordinal) != 0) + { + this.RenameDirectory(Path.Combine(this.ProjectMgr.ProjectFolder, newPath)); + } + this.VirtualNodeName = newPath; + + this.ItemNode.Rename(VirtualNodeName); + + // Let all children know of the new path + for(HierarchyNode child = this.FirstChild; child != null; child = child.NextSibling) + { + FolderNode node = child as FolderNode; + + if(node == null) + { + child.SetEditLabel(child.Caption); + } + else + { + node.RenameFolder(node.Caption); + } + } + + // Some of the previous operation may have changed the selection so set it back to us + IVsUIHierarchyWindow uiWindow = UIHierarchyUtilities.GetUIHierarchyWindow(this.ProjectMgr.Site, SolutionExplorer); + ErrorHandler.ThrowOnFailure(uiWindow.ExpandItem(this.ProjectMgr, this.ID, EXPANDFLAGS.EXPF_SelectItem)); + + } + + /// + /// Show error message if not in automation mode, otherwise throw exception + /// + /// path of file or folder already existing on disk + /// S_OK + private int ShowFileOrFolderAlreadExistsErrorMessage(string newPath) + { + //A file or folder with the name '{0}' already exists on disk at this location. Please choose another name. + //If this file or folder does not appear in the Solution Explorer, then it is not currently part of your project. To view files which exist on disk, but are not in the project, select Show All Files from the Project menu. + string errorMessage = (String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.FileOrFolderAlreadyExists, CultureInfo.CurrentUICulture), newPath)); + if(!Utilities.IsInAutomationFunction(this.ProjectMgr.Site)) + { + string title = null; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + VsShellUtilities.ShowMessageBox(this.ProjectMgr.Site, title, errorMessage, icon, buttons, defaultButton); + return VSConstants.S_OK; + } + else + { + throw new InvalidOperationException(errorMessage); + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/GlobalPropertyHandler.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/GlobalPropertyHandler.cs new file mode 100644 index 0000000000..66f5a67047 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/GlobalPropertyHandler.cs @@ -0,0 +1,365 @@ +/*************************************************************************** + +Copyright (c) Microsoft Corporation. All rights reserved. +This code is licensed under the Visual Studio SDK license terms. +THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. + +***************************************************************************/ +extern alias Shell10; +using System; +using System.Diagnostics; +using System.IO; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.Win32; +using IServiceProvider = System.IServiceProvider; +using MSBuild = Microsoft.Build.BuildEngine; +using VSRegistry = Shell10.Microsoft.VisualStudio.Shell.VSRegistry; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// This class defines and sets the so called global properties that are needed to be provided + /// before a project builds. + /// + internal class GlobalPropertyHandler : IDisposable + { + #region constants + /// + /// The registry relative path entry for finding the fxcop installdir + /// + private const string FxCopRegistryRelativePathEntry = "Setup\\EDev"; + + /// + /// The registry installation Directory key name. + /// + private const string FxCopRegistryInstallDirKeyName = "FxCopDir"; + + /// + /// This is the constant that will be set as the value of the VSIDEResolvedNonMSBuildProjectOutputs global property. + /// + private const string VSIDEResolvedNonMSBuildProjectOutputsValue = ""; + + + #endregion + + #region fields + /// + /// Raised when the active project configuration for a project in the solution has changed. + /// + internal event EventHandler ActiveConfigurationChanged; + + /// + /// Defines the global properties of the associated build project. + /// + private MSBuild.BuildPropertyGroup globalProjectProperties; + + /// + /// Defines the global properties of the associated build engine. + /// + private MSBuild.BuildPropertyGroup globalEngineProperties; + + /// + /// Flag determining if the object has been disposed. + /// + private bool isDisposed; + + /// + /// Defines an object that will be a mutex for this object for synchronizing thread calls. + /// + private static volatile object Mutex = new object(); + + /// + /// Defines the configuration change listener. + /// + private UpdateConfigPropertiesListener configurationChangeListener; + #endregion + + #region constructors + /// + /// Overloaded constructor. + /// + /// An instance of a build project + /// Is thrown if the passed Project is null. + internal GlobalPropertyHandler(MSBuild.Project project) + { + Debug.Assert(project != null, "The project parameter passed cannot be null"); + + this.globalProjectProperties = project.GlobalProperties; + + Debug.Assert(project.ParentEngine != null, "The parent engine has not been initialized"); + + this.globalEngineProperties = project.ParentEngine.GlobalProperties; + } + #endregion + + #region IDisposable Members + + /// + /// The IDispose interface Dispose method for disposing the object determinastically. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + + #region methods + /// + /// Initializes MSBuild project properties. This method is called before the first project re-evaluation happens in order to set the global properties. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")] + internal virtual void InitializeGlobalProperties() + { + + // Set the BuildingInsideVisualStudio property to true. + this.SetGlobalProperty(GlobalProperty.BuildingInsideVisualStudio.ToString(), "true"); + + // Set the ResolvedNonMSBuildProjectOutputs property to empty. This is so that it has some deterministic value, even + // if it's empty. This is important because of the way global properties are merged together when one + // project is calling the task on another project. + this.SetGlobalProperty(GlobalProperty.VSIDEResolvedNonMSBuildProjectOutputs.ToString(), VSIDEResolvedNonMSBuildProjectOutputsValue); + + // Set the RunCodeAnalysisOverride property to false. This is so that it has some deterministic value. + // This is important because of the way global properties are merged together when one + // project is calling the task on another project. + this.SetGlobalProperty(GlobalProperty.RunCodeAnalysisOnce.ToString(), "false"); + + // Set Configuration=Debug. This is a perf optimization, not strictly required for correct functionality. + // Since most people keep most of their projects with Active Configuration = "Debug" during development, + // setting this up front makes it faster to load the project. This way, we don't have to change the + // value of Configuration down the road, forcing MSBuild to have to re-evaluate the project. + this.SetGlobalProperty(GlobalProperty.Configuration.ToString(), ProjectConfig.Debug); + + // Set Platform=AnyCPU. This is a perf optimization, not strictly required for correct functionality. + // Since most people keep most of their projects with Active Platform = "AnyCPU" during development, + // setting this up front makes it faster to load the project. This way, we don't have to change the + // value of Platform down the road, forcing MSBuild to have to re-evaluate the project. + this.SetGlobalProperty(GlobalProperty.Platform.ToString(), ProjectConfig.AnyCPU); + + // Set the solution related msbuild global properties. + this.SetSolutionProperties(); + + // Set the VS location global property. + this.SetGlobalProperty(GlobalProperty.DevEnvDir.ToString(), GetEnvironmentDirectoryLocation()); + + // Set the fxcop location global property. + this.SetGlobalProperty(GlobalProperty.FxCopDir.ToString(), GetFxCopDirectoryLocation()); + } + + /// + /// Initializes the internal configuration change listener. + /// + /// The associated service hierarchy. + /// The associated service provider. + internal void RegisterConfigurationChangeListener(IVsHierarchy hierarchy, IServiceProvider serviceProvider) + { + Debug.Assert(hierarchy != null, "The passed hierarchy cannot be null"); + Debug.Assert(serviceProvider != null, "The passed service provider cannot be null"); + Debug.Assert(this.configurationChangeListener == null, "The configuration change listener has already been initialized"); + this.configurationChangeListener = new UpdateConfigPropertiesListener(this, serviceProvider); + } + + /// + /// The method that does the cleanup. + /// + /// true if called from IDispose.Dispose; false if called from Finalizer. + protected virtual void Dispose(bool disposing) + { + // Everybody can go here. + if(!this.isDisposed) + { + // Synchronize calls to the Dispose simultaniously. + lock(Mutex) + { + if(disposing) + { + this.configurationChangeListener.Dispose(); + } + + this.isDisposed = true; + } + } + } + + /// + /// Called when the active project configuration for a project in the solution has changed. + /// + /// The project whose configuration has changed. + private void RaiseActiveConfigurationChanged(IVsHierarchy hierarchy) + { + // Save event in temporary variable to avoid race condition. + EventHandler tempEvent = this.ActiveConfigurationChanged; + if(tempEvent != null) + { + tempEvent(this, new ActiveConfigurationChangedEventArgs(hierarchy)); + } + } + + /// + /// Sets the solution related global properties (SolutionName, SolutionFileName, SolutionPath, SolutionDir, SolutionExt). + /// + private void SetSolutionProperties() + { + IVsSolution solution = Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(IVsSolution)) as IVsSolution; + Debug.Assert(solution != null, "Could not retrieve the solution service from the global service provider"); + + string solutionDirectory, solutionFile, userOptionsFile; + + // We do not want to throw. If we cannot set the solution related constants we set them to empty string. + ErrorHandler.ThrowOnFailure(solution.GetSolutionInfo(out solutionDirectory, out solutionFile, out userOptionsFile)); + + if(solutionDirectory == null) + { + solutionDirectory = String.Empty; + } + + this.SetGlobalProperty(GlobalProperty.SolutionDir.ToString(), solutionDirectory); + + if(solutionFile == null) + { + solutionFile = String.Empty; + } + + this.SetGlobalProperty(GlobalProperty.SolutionPath.ToString(), solutionFile); + + string solutionFileName = (solutionFile.Length == 0) ? String.Empty : Path.GetFileName(solutionFile); + this.SetGlobalProperty(GlobalProperty.SolutionFileName.ToString(), solutionFileName); + + string solutionName = (solutionFile.Length == 0) ? String.Empty : Path.GetFileNameWithoutExtension(solutionFile); + this.SetGlobalProperty(GlobalProperty.SolutionName.ToString(), solutionName); + + string solutionExtension = String.Empty; + if(solutionFile.Length > 0 && Path.HasExtension(solutionFile)) + { + solutionExtension = Path.GetExtension(solutionFile); + } + + this.SetGlobalProperty(GlobalProperty.SolutionExt.ToString(), solutionExtension); + } + + /// + /// Retrieves the Devenv installation directory. + /// + private static string GetEnvironmentDirectoryLocation() + { + IVsShell shell = Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(IVsShell)) as IVsShell; + Debug.Assert(shell != null, "Could not retrieve the IVsShell service from the global service provider"); + + object installDirAsObject; + + // We do not want to throw. If we cannot set the solution related constants we set them to empty string. + ErrorHandler.ThrowOnFailure(shell.GetProperty((int)__VSSPROPID.VSSPROPID_InstallDirectory, out installDirAsObject)); + + string installDir = ((string)installDirAsObject); + + if(String.IsNullOrEmpty(installDir)) + { + return String.Empty; + } + + // Ensure that we have traimnling backslash as this is done for the langproj macros too. + if(installDir[installDir.Length - 1] != Path.DirectorySeparatorChar) + { + installDir += Path.DirectorySeparatorChar; + } + + return installDir; + } + + + /// + /// Retrieves the fxcop dierctory location + /// + private static string GetFxCopDirectoryLocation() + { + using(RegistryKey root = VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_Configuration)) + { + if(null == root) + { + return String.Empty; + } + + using(RegistryKey key = root.OpenSubKey(FxCopRegistryRelativePathEntry)) + { + if(key != null) + { + string fxcopInstallDir = key.GetValue(FxCopRegistryInstallDirKeyName) as string; + + return (fxcopInstallDir == null) ? String.Empty : fxcopInstallDir; + } + } + } + + return String.Empty; + } + + /// + /// Sets a global property on the associated build project and build engine. + /// + /// The name of teh property to set. + /// Teh value of teh property. + private void SetGlobalProperty(string propertyName, string propertyValue) + { + this.globalProjectProperties.SetProperty(propertyName, propertyValue, true); + + // Set the same global property on the parent Engine object. The Project + // object, when it was created, got a clone of the global properties from + // the engine. So changing it in the Project doesn't impact the Engine. + // However, we do need the Engine to have this new global property setting + // as well, because with project-to-project references, any child projects + // are going to get their initial global properties from the Engine when + // they are created. + this.globalEngineProperties.SetProperty(propertyName, propertyValue, true); + } + #endregion + + #region nested types + /// + /// Defines a class that will listen to configuration changes and will update platform and configuration name changes accordingly. + /// + private class UpdateConfigPropertiesListener : UpdateSolutionEventsListener + { + #region fields + + /// + /// Defines the containing object. + /// + private GlobalPropertyHandler globalPropertyHandler; + #endregion + + #region constructors + /// + /// Overloaded constructor. + /// + /// + /// The associated hierrachy. + /// The associated service provider + internal UpdateConfigPropertiesListener(GlobalPropertyHandler globalPropertyHandler, IServiceProvider serviceProvider) + : base(serviceProvider) + { + this.globalPropertyHandler = globalPropertyHandler; + } + #endregion + + #region methods + /// + /// Called when the active project configuration for a project in the solution has changed. + /// + /// The project whose configuration has changed. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public override int OnActiveProjectCfgChange(IVsHierarchy hierarchy) + { + this.globalPropertyHandler.RaiseActiveConfigurationChanged(hierarchy); + return VSConstants.S_OK; + } + #endregion + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/GlobalSuppressions.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/GlobalSuppressions.cs new file mode 100644 index 0000000000..6f5c24b31f --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/GlobalSuppressions.cs @@ -0,0 +1,622 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Diagnostics.CodeAnalysis; + +// This file is used by Code Analysis to maintain SuppressMessage +// attributes that are applied to this project. Project-level +// suppressions either have no target or are given a specific target +// and scoped to a namespace, type, member, etc. +// +// To add a suppression to this file, right-click the message in the +// Error List, point to "Suppress Message(s)", and click "In Project +// Suppression File". You do not need to add suppressions to this +// file manually. + +// The below contains suppressions from LinkDemand occurring from calls to the Marshal type's methods. +// Calls to these methods are absolute necessary to do successful VS integration. +// All these methods are called from the VS Kernel, through interface calls that are expanded in the below methods. +// Some of the below methods are non public but their caller's then are methods that are VS Integration interface implementations. +// The methods in which the LinkDemands were suppressed should be revisited though if adding further LinkDemands or directly Demanding UnmanagedCode Permission is a better choice. + +//Interface parameters naming matching suppressions +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#AdviseHierarchyEvents(Microsoft.VisualStudio.Shell.Interop.IVsHierarchyEvents,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#AdviseHierarchyEvents(Microsoft.VisualStudio.Shell.Interop.IVsHierarchyEvents,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#AdviseBuildStatusCallback(Microsoft.VisualStudio.Shell.Interop.IVsBuildStatusCallback,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#QueryStartBuild(System.UInt32,System.Int32[],System.Int32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#QueryStartBuild(System.UInt32,System.Int32[],System.Int32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#QueryStartClean(System.UInt32,System.Int32[],System.Int32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#QueryStartUpToDateCheck(System.UInt32,System.Int32[],System.Int32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#QueryStartClean(System.UInt32,System.Int32[],System.Int32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#QueryStartUpToDateCheck(System.UInt32,System.Int32[],System.Int32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#QueryStartUpToDateCheck(System.UInt32,System.Int32[],System.Int32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#QueryStatus(System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#StartBuild(Microsoft.VisualStudio.Shell.Interop.IVsOutputWindowPane,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#StartBuild(Microsoft.VisualStudio.Shell.Interop.IVsOutputWindowPane,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#StartClean(Microsoft.VisualStudio.Shell.Interop.IVsOutputWindowPane,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#UnadviseBuildStatusCallback(System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#Stop(System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#StartUpToDateCheck(Microsoft.VisualStudio.Shell.Interop.IVsOutputWindowPane,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#StartUpToDateCheck(Microsoft.VisualStudio.Shell.Interop.IVsOutputWindowPane,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#StartClean(Microsoft.VisualStudio.Shell.Interop.IVsOutputWindowPane,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildDependency.#get_HelpContext(System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildDependency.#get_Description(System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildDependency.#get_CanonicalName(System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#Wait(System.UInt32,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildDependency.#get_MustUpdateBefore(System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildDependency.#get_HelpFile(System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnAfterLoadProject(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsHierarchy)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnAfterOpeningChildren(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnAfterLoadProject(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsHierarchy)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnAfterClosingChildren(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnAfterOpenProject(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnAfterOpenProject(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnAfterRenameProject(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnBeforeCloseProject(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnBeforeCloseProject(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnBeforeClosingChildren(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnBeforeOpeningChildren(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnBeforeUnloadProject(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsHierarchy)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnBeforeUnloadProject(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsHierarchy)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnQueryChangeProjectParent(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnQueryChangeProjectParent(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnQueryChangeProjectParent(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnQueryCloseProject(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,System.Int32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnQueryCloseProject(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,System.Int32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnQueryCloseProject(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,System.Int32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnQueryCloseSolution(System.Object,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnQueryUnloadProject(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListenerForProjectOpen.#OnAfterOpenSolution(System.Object,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListenerForProjectOpen.#OnAfterOpenSolution(System.Object,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#OnActiveProjectCfgChange(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#OnAfterActiveSolutionCfgChange(Microsoft.VisualStudio.Shell.Interop.IVsCfg,Microsoft.VisualStudio.Shell.Interop.IVsCfg)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#OnAfterActiveSolutionCfgChange(Microsoft.VisualStudio.Shell.Interop.IVsCfg,Microsoft.VisualStudio.Shell.Interop.IVsCfg)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#OnBeforeActiveSolutionCfgChange(Microsoft.VisualStudio.Shell.Interop.IVsCfg,Microsoft.VisualStudio.Shell.Interop.IVsCfg)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#OnBeforeActiveSolutionCfgChange(Microsoft.VisualStudio.Shell.Interop.IVsCfg,Microsoft.VisualStudio.Shell.Interop.IVsCfg)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#UpdateProjectCfg_Begin(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsCfg,Microsoft.VisualStudio.Shell.Interop.IVsCfg,System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "4#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#UpdateProjectCfg_Begin(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsCfg,Microsoft.VisualStudio.Shell.Interop.IVsCfg,System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#UpdateProjectCfg_Begin(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsCfg,Microsoft.VisualStudio.Shell.Interop.IVsCfg,System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#UpdateProjectCfg_Begin(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsCfg,Microsoft.VisualStudio.Shell.Interop.IVsCfg,System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#UpdateProjectCfg_Begin(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsCfg,Microsoft.VisualStudio.Shell.Interop.IVsCfg,System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#UpdateProjectCfg_Done(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsCfg,Microsoft.VisualStudio.Shell.Interop.IVsCfg,System.UInt32,System.Int32,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "5#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#UpdateProjectCfg_Done(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsCfg,Microsoft.VisualStudio.Shell.Interop.IVsCfg,System.UInt32,System.Int32,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#UpdateProjectCfg_Done(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsCfg,Microsoft.VisualStudio.Shell.Interop.IVsCfg,System.UInt32,System.Int32,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#UpdateProjectCfg_Done(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsCfg,Microsoft.VisualStudio.Shell.Interop.IVsCfg,System.UInt32,System.Int32,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#UpdateProjectCfg_Done(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsCfg,Microsoft.VisualStudio.Shell.Interop.IVsCfg,System.UInt32,System.Int32,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "4#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#UpdateProjectCfg_Done(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsCfg,Microsoft.VisualStudio.Shell.Interop.IVsCfg,System.UInt32,System.Int32,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#UpdateSolution_Begin(System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.UpdateSolutionEventsListener.#UpdateSolution_StartUpdate(System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#DeleteItem(System.UInt32,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#DeleteItem(System.UInt32,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#Exec(System.Guid&,System.UInt32,System.UInt32,System.IntPtr,System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#Exec(System.Guid&,System.UInt32,System.UInt32,System.IntPtr,System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#Exec(System.Guid&,System.UInt32,System.UInt32,System.IntPtr,System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#ExecCommand(System.UInt32,System.Guid&,System.UInt32,System.UInt32,System.IntPtr,System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#ExecCommand(System.UInt32,System.Guid&,System.UInt32,System.UInt32,System.IntPtr,System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#ExecCommand(System.UInt32,System.Guid&,System.UInt32,System.UInt32,System.IntPtr,System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#ExecCommand(System.UInt32,System.Guid&,System.UInt32,System.UInt32,System.IntPtr,System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "5#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#ExecCommand(System.UInt32,System.Guid&,System.UInt32,System.UInt32,System.IntPtr,System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "4#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#ExecCommand(System.UInt32,System.Guid&,System.UInt32,System.UInt32,System.IntPtr,System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetCanonicalName(System.UInt32,System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetCanonicalName(System.UInt32,System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetGuidProperty(System.UInt32,System.Int32,System.Guid&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetGuidProperty(System.UInt32,System.Int32,System.Guid&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetNestedHierarchy(System.UInt32,System.Guid&,System.IntPtr&,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetNestedHierarchy(System.UInt32,System.Guid&,System.IntPtr&,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetProperty(System.UInt32,System.Int32,System.Object&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetProperty(System.UInt32,System.Int32,System.Object&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetProperty(System.UInt32,System.Int32,System.Object&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#IgnoreItemFileChanges(System.UInt32,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#IgnoreItemFileChanges(System.UInt32,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#IsItemDirty(System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#IsItemDirty(System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#IsItemDirty(System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#IsItemReloadable(System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#IsItemReloadable(System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#OnBeforeDropNotify(Microsoft.VisualStudio.OLE.Interop.IDataObject,System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#ParseCanonicalName(System.String,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#ParseCanonicalName(System.String,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#QueryClose(System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#QueryDeleteItem(System.UInt32,System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#QueryDeleteItem(System.UInt32,System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#QueryDeleteItem(System.UInt32,System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#QueryStatus(System.Guid&,System.UInt32,Microsoft.VisualStudio.OLE.Interop.OLECMD[],System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#QueryStatusCommand(System.UInt32,System.Guid&,System.UInt32,Microsoft.VisualStudio.OLE.Interop.OLECMD[],System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#QueryStatusCommand(System.UInt32,System.Guid&,System.UInt32,Microsoft.VisualStudio.OLE.Interop.OLECMD[],System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#QueryStatusCommand(System.UInt32,System.Guid&,System.UInt32,Microsoft.VisualStudio.OLE.Interop.OLECMD[],System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#ReloadItem(System.UInt32,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#ReloadItem(System.UInt32,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "4#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#SaveItem(Microsoft.VisualStudio.Shell.Interop.VSSAVEFLAGS,System.String,System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#SaveItem(Microsoft.VisualStudio.Shell.Interop.VSSAVEFLAGS,System.String,System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#SaveItem(Microsoft.VisualStudio.Shell.Interop.VSSAVEFLAGS,System.String,System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#SaveItem(Microsoft.VisualStudio.Shell.Interop.VSSAVEFLAGS,System.String,System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#SetGuidProperty(System.UInt32,System.Int32,System.Guid&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#SetGuidProperty(System.UInt32,System.Int32,System.Guid&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#SetProperty(System.UInt32,System.Int32,System.Object)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#SetProperty(System.UInt32,System.Int32,System.Object)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#UnadviseHierarchyEvents(System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OANavigableProjectItems.#AddFolder(System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OANavigableProjectItems.#AddFolder(System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OANavigableProjectItems.#AddFromDirectory(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OANavigableProjectItems.#AddFromFile(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OANavigableProjectItems.#AddFromFileCopy(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OANavigableProjectItems.#AddFromTemplate(System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OANavigableProjectItems.#AddFromTemplate(System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAProjectItem`1.#Save(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAProjectItem`1.#SaveAs(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#EnumOutputs(Microsoft.VisualStudio.Shell.Interop.IVsEnumOutputs&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#get_BuildableProjectCfg(Microsoft.VisualStudio.Shell.Interop.IVsBuildableProjectCfg&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#get_CanonicalName(System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#get_DisplayName(System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#get_IsDebugOnly(System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#get_IsPackaged(System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#get_IsReleaseOnly(System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#get_IsSpecifyingOutputSupported(System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#get_Platform(System.Guid&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#get_ProjectCfgProvider(Microsoft.VisualStudio.Shell.Interop.IVsProjectCfgProvider&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#get_RootURL(System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#get_TargetCodePage(System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#get_UpdateSequenceNumber(Microsoft.VisualStudio.OLE.Interop.ULARGE_INTEGER[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#GetCfg(Microsoft.VisualStudio.Shell.Interop.IVsCfg&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#GetPages(Microsoft.VisualStudio.OLE.Interop.CAUUID[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#GetProjectDesignerPages(Microsoft.VisualStudio.OLE.Interop.CAUUID[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#GetProjectItem(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy&,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#GetProjectItem(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy&,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#OpenOutput(System.String,Microsoft.VisualStudio.Shell.Interop.IVsOutput&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#OpenOutput(System.String,Microsoft.VisualStudio.Shell.Interop.IVsOutput&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#QueryDebugLaunch(System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#QueryDebugLaunch(System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnAfterAsynchOpenProject(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnAfterAsynchOpenProject(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnAfterChangeProjectParent(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListener.#OnAfterCloseSolution(System.Object)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#AdviseBuildStatusCallback(Microsoft.VisualStudio.Shell.Interop.IVsBuildStatusCallback,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildDependency.#get_ReferredProject(System.Object&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#QueryStartClean(System.UInt32,System.Int32[],System.Int32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#QueryStartBuild(System.UInt32,System.Int32[],System.Int32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#get_ProjectCfg(Microsoft.VisualStudio.Shell.Interop.IVsProjectCfg&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildDependency.#get_Type(System.Guid&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1701:ResourceStringCompoundWordsShouldBeCasedCorrectly", MessageId = "dataset", Scope = "resource", Target = "Microsoft.VisualStudio.Project.resources")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1703:ResourceStringsShouldBeSpelledCorrectly", MessageId = "Intelli", Scope = "resource", Target = "Microsoft.VisualStudio.Project.SecurityWarningDialog.resources")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.FileNode.#AfterSaveItemAs(System.IntPtr,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectNode.#IsItemDirty(System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectNode.#OnChanged(System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectNode.#OnRequestEdit(System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectNode.#SaveItem(Microsoft.VisualStudio.Shell.Interop.VSSAVEFLAGS,System.String,System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "4#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectNode.#SaveItem(Microsoft.VisualStudio.Shell.Interop.VSSAVEFLAGS,System.String,System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectNode.#SaveItem(Microsoft.VisualStudio.Shell.Interop.VSSAVEFLAGS,System.String,System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NodeProperties.#GetCfgProvider(Microsoft.VisualStudio.Shell.Interop.IVsCfgProvider&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NodeProperties.#GetPages(Microsoft.VisualStudio.OLE.Interop.CAUUID[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NodeProperties.#GetProjectDesignerPages(Microsoft.VisualStudio.OLE.Interop.CAUUID[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NodeProperties.#GetProjectItem(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy&,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NodeProperties.#GetProjectItem(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy&,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#OnBeforeDropNotify(Microsoft.VisualStudio.OLE.Interop.IDataObject,System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#OnClear(System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#OnPaste(System.Int32,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#OnPaste(System.Int32,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#RegisterClipboardNotifications(System.Boolean)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SettingsPage.#GetPageInfo(Microsoft.VisualStudio.OLE.Interop.PROPPAGEINFO[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SettingsPage.#Help(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SettingsPage.#Move(Microsoft.VisualStudio.OLE.Interop.RECT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SettingsPage.#SetObjects(System.UInt32,System.Object[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SettingsPage.#SetObjects(System.UInt32,System.Object[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SettingsPage.#SetPageSite(Microsoft.VisualStudio.OLE.Interop.IPropertyPageSite)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SettingsPage.#Show(System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SettingsPage.#TranslateAccelerator(Microsoft.VisualStudio.OLE.Interop.MSG[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "4#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SingleFileGeneratorFactory.#CreateGeneratorInstance(System.String,System.Int32&,System.Int32&,System.Int32&,Microsoft.VisualStudio.Shell.Interop.IVsSingleFileGenerator&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SingleFileGeneratorFactory.#CreateGeneratorInstance(System.String,System.Int32&,System.Int32&,System.Int32&,Microsoft.VisualStudio.Shell.Interop.IVsSingleFileGenerator&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SingleFileGeneratorFactory.#CreateGeneratorInstance(System.String,System.Int32&,System.Int32&,System.Int32&,Microsoft.VisualStudio.Shell.Interop.IVsSingleFileGenerator&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SingleFileGeneratorFactory.#CreateGeneratorInstance(System.String,System.Int32&,System.Int32&,System.Int32&,Microsoft.VisualStudio.Shell.Interop.IVsSingleFileGenerator&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SingleFileGeneratorFactory.#CreateGeneratorInstance(System.String,System.Int32&,System.Int32&,System.Int32&,Microsoft.VisualStudio.Shell.Interop.IVsSingleFileGenerator&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SingleFileGeneratorFactory.#GetDefaultGenerator(System.String,System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SingleFileGeneratorFactory.#GetDefaultGenerator(System.String,System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SingleFileGeneratorFactory.#GetGeneratorInformation(System.String,System.Int32&,System.Int32&,System.Int32&,System.Guid&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SingleFileGeneratorFactory.#GetGeneratorInformation(System.String,System.Int32&,System.Int32&,System.Int32&,System.Guid&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "4#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SingleFileGeneratorFactory.#GetGeneratorInformation(System.String,System.Int32&,System.Int32&,System.Int32&,System.Guid&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SingleFileGeneratorFactory.#GetGeneratorInformation(System.String,System.Int32&,System.Int32&,System.Int32&,System.Guid&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SingleFileGeneratorFactory.#GetGeneratorInformation(System.String,System.Int32&,System.Int32&,System.Int32&,System.Guid&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectNode.#IsItemDirty(System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SettingsPage.#Activate(System.IntPtr,Microsoft.VisualStudio.OLE.Interop.RECT[],System.Int32)")] + +//Ref arguments suppressions +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.TokenProcessor.#ReplaceBetweenTokens(System.String&,Microsoft.VisualStudio.Project.ReplaceBetweenPairToken)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.TokenProcessor.#ReplaceTokens(System.String&,Microsoft.VisualStudio.Project.ReplacePairToken)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#QueryStatusOnNode(System.Guid,System.UInt32,System.IntPtr,Microsoft.VisualStudio.Project.QueryStatusResult&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.DocumentManager.#OpenWithSpecific(System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&,Microsoft.VisualStudio.Project.WindowFrameShowAction)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.FileDocumentManager.#Open(System.Boolean,System.Boolean,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&,Microsoft.VisualStudio.Project.WindowFrameShowAction)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.DocumentManager.#OpenWithSpecific(System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&,Microsoft.VisualStudio.Project.WindowFrameShowAction)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.TokenProcessor.#DeleteTokens(System.String&,Microsoft.VisualStudio.Project.DeleteToken)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.DocumentManager.#Open(System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&,Microsoft.VisualStudio.Project.WindowFrameShowAction)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.DocumentManager.#CloseWindowFrame(Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "4#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Load(System.String,System.String,System.String,System.UInt32,System.Guid&,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1045:DoNotPassTypesByReference", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#SetGuidProperty(System.Int32,System.Guid&)")] + +//Out arguments suppressions +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.DocumentManager.#Open(System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&,Microsoft.VisualStudio.Project.WindowFrameShowAction)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "4#", Scope = "member", Target = "Microsoft.VisualStudio.Project.FileDocumentManager.#Open(System.Boolean,System.Boolean,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&,Microsoft.VisualStudio.Project.WindowFrameShowAction)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#DisableCommandOnNodesThatDoNotSupportMultiSelection(System.Guid,System.UInt32,System.Collections.Generic.IList`1,System.Boolean&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "6#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#ExecCommandIndependentOfSelection(System.Guid,System.UInt32,System.UInt32,System.IntPtr,System.IntPtr,Microsoft.VisualStudio.Project.CommandOrigin,System.Boolean&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "7#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#ExecCommandThatDependsOnSelectedNodes(System.Guid,System.UInt32,System.UInt32,System.IntPtr,System.IntPtr,Microsoft.VisualStudio.Project.CommandOrigin,System.Collections.Generic.IList`1,System.Boolean&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#QueryStatusCommandFromOleCommandTarget(System.Guid,System.UInt32,System.Boolean&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetGuidProperty(System.Int32,System.Guid&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "5#", Scope = "member", Target = "Microsoft.VisualStudio.Project.DocumentManager.#OpenWithSpecific(System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&,Microsoft.VisualStudio.Project.WindowFrameShowAction)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.FileDocumentManager.#Open(System.Boolean,System.Boolean,System.Guid,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&,Microsoft.VisualStudio.Project.WindowFrameShowAction)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ReferenceNode.#CanAddReference(Microsoft.VisualStudio.Project.ReferenceNode+CannotAddReferenceErrorMessage&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "5#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Load(System.String,System.String,System.String,System.UInt32,System.Guid&,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "7#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#ExecCommandThatDependsOnSelectedNodes(System.Guid,System.UInt32,System.UInt32,System.IntPtr,System.IntPtr,Microsoft.VisualStudio.Project.CommandOrigin,System.Collections.Generic.IList`1,System.Boolean&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#DisableCommandOnNodesThatDoNotSupportMultiSelection(System.Guid,System.UInt32,System.Collections.Generic.IList`1,System.Boolean&)")] + +//Default constructors for COM suppressions +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.AssemblyReferenceNode")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.ComReferenceNode")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.ConfigProvider")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.ConnectionPointContainer")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.DependentFileNode")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.DependentFileNodeProperties")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.FileNode")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.FileNodeProperties")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.FolderNode")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.FolderNodeProperties")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.HierarchyNode")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.NestedProjectNode")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.NodeProperties")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAAssemblyReference")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAComReference")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAFileItem")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAFolderItem")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OANavigableProjectItems")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OANestedProjectItem")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OANullProperty")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAProject")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAProjectItems")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAProjectReference")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAProperties")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAProperty")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAReferenceFolderItem")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAReferenceItem")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAReferences")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAVSProject")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAVSProjectEvents")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.OutputGroup")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.ProjectConfig")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.ProjectConfigProperties")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.ProjectContainerNode")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.ProjectNode")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.ProjectNodeProperties")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.ProjectPackage")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.ProjectReferenceNode")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.ProjectReferencesProperties")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.ReferenceContainerNode")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.ReferenceNode")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.ReferenceNodeProperties")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.SettingsPage")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.SingleFileGeneratorNodeProperties")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1409:ComVisibleTypesShouldBeCreatable", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAVSProjectItem")] + +//PInvoke suppressions +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1414:MarkBooleanPInvokeArgumentsWithMarshalAs", Scope = "member", Target = "Microsoft.VisualStudio.Project.NativeMethods.#DestroyIcon(System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1414:MarkBooleanPInvokeArgumentsWithMarshalAs", Scope = "member", Target = "Microsoft.VisualStudio.Project.NativeMethods.#GetBinaryType(System.String,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1414:MarkBooleanPInvokeArgumentsWithMarshalAs", Scope = "member", Target = "Microsoft.VisualStudio.Project.UnsafeNativeMethods.#GlobalUnlock(System.Runtime.InteropServices.HandleRef)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1414:MarkBooleanPInvokeArgumentsWithMarshalAs", Scope = "member", Target = "Microsoft.VisualStudio.Project.UnsafeNativeMethods.#GlobalUnLock(System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1414:MarkBooleanPInvokeArgumentsWithMarshalAs", Scope = "member", Target = "Microsoft.VisualStudio.Project.UnsafeNativeMethods.#ImageList_Draw(System.Runtime.InteropServices.HandleRef,System.Int32,System.Runtime.InteropServices.HandleRef,System.Int32,System.Int32,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1414:MarkBooleanPInvokeArgumentsWithMarshalAs", Scope = "member", Target = "Microsoft.VisualStudio.Project.UnsafeNativeMethods.#SHGetPathFromIDList(System.IntPtr,System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Interoperability", "CA1414:MarkBooleanPInvokeArgumentsWithMarshalAs", Scope = "member", Target = "Microsoft.VisualStudio.Project.NativeMethods.#IsDialogMessageA(System.IntPtr,Microsoft.VisualStudio.OLE.Interop.MSG&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", MessageId = "return", Scope = "member", Target = "Microsoft.VisualStudio.Project.UnsafeNativeMethods.#RegisterClipboardFormat(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", MessageId = "return", Scope = "member", Target = "Microsoft.VisualStudio.Project.UnsafeNativeMethods.#GlobalSize(System.IntPtr)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Portability", "CA1901:PInvokeDeclarationsShouldBePortable", MessageId = "return", Scope = "member", Target = "Microsoft.VisualStudio.Project.UnsafeNativeMethods.#GlobalSize(System.Runtime.InteropServices.HandleRef)")] + +//'Flags' naming suppressions +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Scope = "type", Target = "Microsoft.VisualStudio.Project.ModuleKindFlags")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectNode.#VirtualProjectFlags")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetQueryRemoveFileFlags(System.String[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetQueryAddFileFlags(System.String[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetAddFileFlags(System.String[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "flags", Scope = "member", Target = "Microsoft.VisualStudio.Project.Utilities.#CreateCADWORD(System.Collections.Generic.IList`1)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "flags", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Load(System.String,System.String,System.String,System.UInt32,System.Guid&,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectContainerNode.#AddNestedProjectFromTemplate(System.String,System.String,System.String,Microsoft.VisualStudio.Project.ProjectElement,Microsoft.VisualStudio.Shell.Interop.__VSCREATEPROJFLAGS)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectContainerNode.#AddNestedProjectFromTemplate(Microsoft.VisualStudio.Project.ProjectElement,Microsoft.VisualStudio.Shell.Interop.__VSCREATEPROJFLAGS)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectContainerNode.#AddExistingNestedProject(Microsoft.VisualStudio.Project.ProjectElement,Microsoft.VisualStudio.Shell.Interop.__VSCREATEPROJFLAGS)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectNode.#Init(System.String,System.String,System.String,Microsoft.VisualStudio.Shell.Interop.__VSCREATEPROJFLAGS)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "flags", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#OnPropertyChanged(Microsoft.VisualStudio.Project.HierarchyNode,System.Int32,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flag", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#IgnoreItemFileChanges(System.Boolean)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "flags", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetSccSpecialFiles(System.String,System.Collections.Generic.IList`1,System.Collections.Generic.IList`1)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "flags", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetSccFiles(System.Collections.Generic.IList`1,System.Collections.Generic.IList`1)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Scope = "member", Target = "Microsoft.VisualStudio.Project.DocumentManager.#OpenWithSpecific(System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&,Microsoft.VisualStudio.Project.WindowFrameShowAction)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flag", Scope = "member", Target = "Microsoft.VisualStudio.Project.DocumentManager.#Close(Microsoft.VisualStudio.Shell.Interop.__FRAMECLOSE)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1726:UsePreferredTerms", MessageId = "Flags", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetRemoveFileFlags(System.String[])")] + +//Properties instead of methods suppressions +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.DocumentManager.#GetFullPathForDocument()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.DocumentManager.#GetOwnerCaption()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetAutomationObject()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetDocumentManager()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetDragTargetHandlerNode()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetEditLabel()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetMkDocument()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetRelationalName()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetRelationNameExtension()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.IReferenceContainerProvider.#GetReferenceContainer()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OANavigableProjectItems.#GetListOfProjectItems()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectElement.#GetFullPathForElement()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetCompiler()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetInner()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetOutputGroupNames()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectOptions.#GetOptionHelp()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1024:UsePropertiesWhereAppropriate", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetSelectedNodes()")] + +//Generic nesting suppressions +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.BuildableProjectConfig.#Build(System.UInt32,Microsoft.VisualStudio.Shell.Interop.IVsOutputWindowPane,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetOutputGroupNames()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1006:DoNotNestGenericTypesInMemberSignatures", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#NewConfigProperties")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1004:GenericMethodsShouldProvideTypeParameter", Scope = "member", Target = "Microsoft.VisualStudio.Project.Utilities.#ConvertFromType`1(System.String,System.Globalization.CultureInfo)")] + +//Methods that swallows exceptions suppressions +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.Web.CodeBehindCodeGenerator.#GetDesignerItem(Microsoft.VisualStudio.Project.Web.VsHierarchyItem,System.Boolean)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.Web.CodeBehindCodeGenerator.#FindClass(Microsoft.VisualStudio.Project.Web.VsHierarchyItem,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.Web.CodeBehindCodeGenerator.#DisposeGenerateState()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.Web.CodeBehindCodeGenerator.#GetFieldNames(EnvDTE.CodeClass,System.Boolean,System.Boolean,System.Int32,System.Int32,Microsoft.VisualStudio.Project.Web.FieldDataDictionary&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.Web.CodeBehindCodeGenerator.#GetFieldNames(System.String[],System.Boolean)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#Close()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.NativeMethods+ConnectionPointCookie.#.ctor(System.Object,System.Object,System.Type,System.Boolean)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.NativeMethods+DataStreamFromComStream.#Flush()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.Web.VsHierarchyItem.#FullPath()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.Web.VsHierarchyItem.#GetGuidPropHelper(Microsoft.VisualStudio.Shell.Interop.__VSHPROPID)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.Web.VsHierarchyItem.#GetPropHelper(System.UInt32,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.Web.VsHierarchyItem.#GetService`1()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.Web.WAUtilities.#CreateInstance`1(System.IServiceProvider,System.Guid)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.Web.WAUtilities.#GetService`1(System.IServiceProvider)")] + +//Local variables names matching instance variable names suppressions +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "dropDataType", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#PasteFromClipboard(Microsoft.VisualStudio.Project.HierarchyNode)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "dropDataType", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Drop(Microsoft.VisualStudio.OLE.Interop.IDataObject,System.UInt32,System.UInt32,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "dropDataType", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#ProcessSelectionDataObject(Microsoft.VisualStudio.OLE.Interop.IDataObject,Microsoft.VisualStudio.Project.HierarchyNode)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "filename", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#InitializeForOuter(System.String,System.String,System.String,System.UInt32,System.Guid&,System.IntPtr&,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "filename", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SaveCompleted(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "isDirty", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#IsDirty(System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "isDirty", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#CleanupSelectionDataObject(System.Boolean,System.Boolean,System.Boolean,System.Boolean)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "isDirty", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#IsFlavorDirty()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "isDirty", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#OnBeforeDropNotify(Microsoft.VisualStudio.OLE.Interop.IDataObject,System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "name", Scope = "member", Target = "Microsoft.VisualStudio.Project.SettingsPage.#GetTypedConfigProperty(System.String,System.Type)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "name", Scope = "member", Target = "Microsoft.VisualStudio.Project.SettingsPage.#GetTypedProperty(System.String,System.Type)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "name", Scope = "member", Target = "Microsoft.VisualStudio.Project.SettingsPage.#SetConfigProperty(System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "options", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetTargetPlatform(Microsoft.VisualStudio.Project.ProjectOptions)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "options", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetOutputAssembly(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "options", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetProjectOptions(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "options", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#PrepareBuild(System.String,System.Boolean)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "options", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetBuildConfigurationProperties(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "project", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#CreateOutputGroup(Microsoft.VisualStudio.Project.ProjectNode,System.Collections.Generic.KeyValuePair`2)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "projectName", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectNode.#Init(System.String,System.String,System.String,Microsoft.VisualStudio.Shell.Interop.__VSCREATEPROJFLAGS)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "sccAuxPath", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetSccLocation(System.String,System.String,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "sccAuxPath", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetSccSettings(System.String,System.String,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "sccLocalPath", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetSccLocation(System.String,System.String,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "sccLocalPath", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetSccSettings(System.String,System.String,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "sccProjectName", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetSccLocation(System.String,System.String,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "sccProjectName", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetSccSettings(System.String,System.String,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "sccProvider", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetSccLocation(System.String,System.String,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "sccProvider", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetSccSettings(System.String,System.String,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "site", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "dropDataType", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#QueryDropEffect(Microsoft.VisualStudio.Project.DropDataType,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "i", Scope = "member", Target = "Microsoft.VisualStudio.Project.EnumSTATDATA.#Microsoft.VisualStudio.OLE.Interop.IEnumSTATDATA.Skip(System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1500:VariableNamesShouldNotMatchFieldNames", MessageId = "i", Scope = "member", Target = "Microsoft.VisualStudio.Project.EnumSTATDATA.#Microsoft.VisualStudio.OLE.Interop.IEnumSTATDATA.Next(System.UInt32,Microsoft.VisualStudio.OLE.Interop.STATDATA[],System.UInt32&)")] + +//COM Exceptions suppressions +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAReferenceBase`1.#ExtenderCATID")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAReferenceBase`1.#Culture")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAReferenceBase`1.#ExtenderNames")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAReferenceBase`1.#Identity")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAReferenceBase`1.#Name")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAReferenceBase`1.#PublicKeyToken")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAReferenceBase`1.#Type")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OASolutionFolder`1.#Parent")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAProjectItem`1.#Collection")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAProjectItem`1.#IsDirty")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAReferenceBase`1.#CopyLocal")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.DataObject.#Microsoft.VisualStudio.OLE.Interop.IDataObject.GetCanonicalFormatEtc(Microsoft.VisualStudio.OLE.Interop.FORMATETC[],Microsoft.VisualStudio.OLE.Interop.FORMATETC[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#.ctor(Microsoft.VisualStudio.Project.ProjectNode,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectContainerNode.#RunVsTemplateWizard(Microsoft.VisualStudio.Project.ProjectElement,System.Boolean)")] + +//IEnumerable implementation suppressions +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAReferences")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAProperties")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OANavigableProjectItems")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1010:CollectionsShouldImplementGenericInterface", Scope = "type", Target = "Microsoft.VisualStudio.Project.Automation.OAProjectItems")] + +//Other suppressions +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible", Scope = "type", Target = "Microsoft.VisualStudio.Project.ProjectNode+ImageName")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "trailer", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetProperty(System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1804:RemoveUnusedLocals", MessageId = "vspropId", Scope = "member", Target = "Microsoft.VisualStudio.Project.HierarchyNode.#GetGuidProperty(System.UInt32,System.Int32,System.Guid&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1805:DoNotInitializeUnnecessarily", Scope = "member", Target = "Microsoft.VisualStudio.Project.NativeMethods+ConnectionPointCookie.#.ctor(System.Object,System.Object,System.Type,System.Boolean)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConnectionPointContainer.#Microsoft.VisualStudio.OLE.Interop.IConnectionPointContainer.FindConnectionPoint(System.Guid&,Microsoft.VisualStudio.OLE.Interop.IConnectionPoint&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.NodeProperties.#EnvDTE80.IInternalExtenderProvider.CanExtend(System.String,System.String,System.Object)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.NodeProperties.#EnvDTE80.IInternalExtenderProvider.GetExtender(System.String,System.String,System.Object,EnvDTE.IExtenderSite,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.NodeProperties.#EnvDTE80.IInternalExtenderProvider.GetExtenderNames(System.String,System.Object)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#Microsoft.VisualStudio.Shell.Interop.IVsProjectFlavorCfg.Close()")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#VisualStudio.Project.IProjectEventsProvider.ProjectEventsProvider")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Microsoft.VisualStudio.Shell.Interop.IVsBuildPropertyStorage.GetItemAttribute(System.UInt32,System.String,System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Microsoft.VisualStudio.Shell.Interop.IVsBuildPropertyStorage.GetPropertyValue(System.String,System.String,System.UInt32,System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Microsoft.VisualStudio.Shell.Interop.IVsBuildPropertyStorage.RemoveProperty(System.String,System.String,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Microsoft.VisualStudio.Shell.Interop.IVsBuildPropertyStorage.SetItemAttribute(System.UInt32,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Microsoft.VisualStudio.Shell.Interop.IVsBuildPropertyStorage.SetPropertyValue(System.String,System.String,System.UInt32,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Microsoft.VisualStudio.Shell.Interop.IVsProjectFlavorCfgProvider.CreateProjectFlavorCfg(Microsoft.VisualStudio.Shell.Interop.IVsCfg,Microsoft.VisualStudio.Shell.Interop.IVsProjectFlavorCfg&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConnectionPointContainer.#Microsoft.VisualStudio.OLE.Interop.IConnectionPointContainer.EnumConnectionPoints(Microsoft.VisualStudio.OLE.Interop.IEnumConnectionPoints&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1033:InterfaceMethodsShouldBeCallableByChildTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Microsoft.VisualStudio.Project.IProjectEventsProvider.ProjectEventsProvider")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetMsBuildProperty(System.String,System.Boolean)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddNodeIfTargetExistInStorage(Microsoft.VisualStudio.Project.HierarchyNode,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddFolderFromOtherProject(System.String,Microsoft.VisualStudio.Project.HierarchyNode)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectElement.#GetMetadataAndThrow(System.String,System.Exception)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectContainerNode.#GetRegisteredProject(Microsoft.VisualStudio.Project.ProjectElement)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2201:DoNotRaiseReservedExceptionTypes", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectConfig.#GetMsBuildProperty(System.String,System.Boolean)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1065:DoNotRaiseExceptionsInUnexpectedLocations", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAVSProject.#Imports")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectBuildDependency.#get_CanonicalName(System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectBuildDependency.#get_HelpContext(System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.SolutionListenerForProjectReferenceUpdate.#OnBeforeUnloadProject(Microsoft.VisualStudio.Shell.Interop.IVsHierarchy,Microsoft.VisualStudio.Shell.Interop.IVsHierarchy)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#TransferItem(System.String,System.String,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#TransferItem(System.String,System.String,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#TransferItem(System.String,System.String,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetSccLocation(System.String,System.String,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetSccLocation(System.String,System.String,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetSccLocation(System.String,System.String,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetSccLocation(System.String,System.String,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetInnerProject(System.Object)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetHostObject(System.String,System.String,System.Object)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetHostObject(System.String,System.String,System.Object)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetHostObject(System.String,System.String,System.Object)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SetAggregateProjectTypeGuids(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SccGlyphChanged(System.Int32,System.UInt32[],Microsoft.VisualStudio.Shell.Interop.VsStateIcon[],System.UInt32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SccGlyphChanged(System.Int32,System.UInt32[],Microsoft.VisualStudio.Shell.Interop.VsStateIcon[],System.UInt32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SccGlyphChanged(System.Int32,System.UInt32[],Microsoft.VisualStudio.Shell.Interop.VsStateIcon[],System.UInt32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SccGlyphChanged(System.Int32,System.UInt32[],Microsoft.VisualStudio.Shell.Interop.VsStateIcon[],System.UInt32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#SaveCompleted(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Save(System.String,System.Int32,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Save(System.String,System.Int32,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Save(System.String,System.Int32,System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#ReopenItem(System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#ReopenItem(System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#ReopenItem(System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "5#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#ReopenItem(System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#ReopenItem(System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "4#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#ReopenItem(System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#RemoveItem(System.UInt32,System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#RemoveItem(System.UInt32,System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#RemoveItem(System.UInt32,System.UInt32,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#OpenItemWithSpecific(System.UInt32,System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "4#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#OpenItemWithSpecific(System.UInt32,System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#OpenItemWithSpecific(System.UInt32,System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "6#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#OpenItemWithSpecific(System.UInt32,System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#OpenItemWithSpecific(System.UInt32,System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#OpenItemWithSpecific(System.UInt32,System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "5#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#OpenItemWithSpecific(System.UInt32,System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#OpenItem(System.UInt32,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#OpenItem(System.UInt32,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#OpenItem(System.UInt32,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#OpenDependency(System.String,Microsoft.VisualStudio.Shell.Interop.IVsDependency&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Load(System.String,System.UInt32,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Load(System.String,System.UInt32,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#Load(System.String,System.UInt32,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#IsDocumentInProject(System.String,System.Int32&,Microsoft.VisualStudio.Shell.Interop.VSDOCUMENTPRIORITY[],System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#IsDocumentInProject(System.String,System.Int32&,Microsoft.VisualStudio.Shell.Interop.VSDOCUMENTPRIORITY[],System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#IsDocumentInProject(System.String,System.Int32&,Microsoft.VisualStudio.Shell.Interop.VSDOCUMENTPRIORITY[],System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#IsDocumentInProject(System.String,System.Int32&,Microsoft.VisualStudio.Shell.Interop.VSDOCUMENTPRIORITY[],System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#IsDirty(System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#InitNew(System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "5#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#InitializeForOuter(System.String,System.String,System.String,System.UInt32,System.Guid&,System.IntPtr&,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#InitializeForOuter(System.String,System.String,System.String,System.UInt32,System.Guid&,System.IntPtr&,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#InitializeForOuter(System.String,System.String,System.String,System.UInt32,System.Guid&,System.IntPtr&,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "4#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#InitializeForOuter(System.String,System.String,System.String,System.UInt32,System.Guid&,System.IntPtr&,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#InitializeForOuter(System.String,System.String,System.String,System.UInt32,System.Guid&,System.IntPtr&,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#InitializeForOuter(System.String,System.String,System.String,System.UInt32,System.Guid&,System.IntPtr&,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "6#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#InitializeForOuter(System.String,System.String,System.String,System.UInt32,System.Guid&,System.IntPtr&,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetSccSpecialFiles(System.UInt32,System.String,Microsoft.VisualStudio.OLE.Interop.CALPOLESTR[],Microsoft.VisualStudio.OLE.Interop.CADWORD[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetSccSpecialFiles(System.UInt32,System.String,Microsoft.VisualStudio.OLE.Interop.CALPOLESTR[],Microsoft.VisualStudio.OLE.Interop.CADWORD[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetSccSpecialFiles(System.UInt32,System.String,Microsoft.VisualStudio.OLE.Interop.CALPOLESTR[],Microsoft.VisualStudio.OLE.Interop.CADWORD[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetSccFiles(System.UInt32,Microsoft.VisualStudio.OLE.Interop.CALPOLESTR[],Microsoft.VisualStudio.OLE.Interop.CADWORD[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetSccFiles(System.UInt32,Microsoft.VisualStudio.OLE.Interop.CALPOLESTR[],Microsoft.VisualStudio.OLE.Interop.CADWORD[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetMkDocument(System.UInt32,System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetMkDocument(System.UInt32,System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetItemContext(System.UInt32,Microsoft.VisualStudio.OLE.Interop.IServiceProvider&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetItemContext(System.UInt32,Microsoft.VisualStudio.OLE.Interop.IServiceProvider&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetFormatList(System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetFile(System.Int32,System.UInt32,System.UInt32&,System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetFile(System.Int32,System.UInt32,System.UInt32&,System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetFile(System.Int32,System.UInt32,System.UInt32&,System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetFile(System.Int32,System.UInt32,System.UInt32&,System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetCurFile(System.String&,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetCurFile(System.String&,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetClassID(System.Guid&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetCfgProvider(Microsoft.VisualStudio.Shell.Interop.IVsCfgProvider&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetBuildSystemKind(System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GetAggregateProjectTypeGuids(System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GenerateUniqueItemName(System.UInt32,System.String,System.String,System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GenerateUniqueItemName(System.UInt32,System.String,System.String,System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GenerateUniqueItemName(System.UInt32,System.String,System.String,System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#GenerateUniqueItemName(System.UInt32,System.String,System.String,System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#EnumDependencies(Microsoft.VisualStudio.Shell.Interop.IVsEnumDependencies&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#BuildTarget(System.String,System.Boolean&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#BuildTarget(System.String,System.Boolean&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "10#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItemWithSpecific(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,System.UInt32,System.Guid&,System.String,System.Guid&,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "8#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItemWithSpecific(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,System.UInt32,System.Guid&,System.String,System.Guid&,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItemWithSpecific(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,System.UInt32,System.Guid&,System.String,System.Guid&,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "9#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItemWithSpecific(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,System.UInt32,System.Guid&,System.String,System.Guid&,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItemWithSpecific(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,System.UInt32,System.Guid&,System.String,System.Guid&,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItemWithSpecific(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,System.UInt32,System.Guid&,System.String,System.Guid&,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItemWithSpecific(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,System.UInt32,System.Guid&,System.String,System.Guid&,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "4#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItemWithSpecific(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,System.UInt32,System.Guid&,System.String,System.Guid&,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "7#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItemWithSpecific(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,System.UInt32,System.Guid&,System.String,System.Guid&,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "6#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItemWithSpecific(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,System.UInt32,System.Guid&,System.String,System.Guid&,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "5#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItemWithSpecific(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,System.UInt32,System.Guid&,System.String,System.Guid&,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "6#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItem(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItem(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItem(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItem(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItem(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "4#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItem(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "5#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddItem(System.UInt32,Microsoft.VisualStudio.Shell.Interop.VSADDITEMOPERATION,System.String,System.UInt32,System.String[],System.IntPtr,Microsoft.VisualStudio.Shell.Interop.VSADDRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectNode.#AddComponent(Microsoft.VisualStudio.Shell.Interop.VSADDCOMPOPERATION,System.UInt32,System.IntPtr[],System.IntPtr,Microsoft.VisualStudio.Shell.Interop.VSADDCOMPRESULT[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectContainerNode.#SaveItem(Microsoft.VisualStudio.Shell.Interop.VSSAVEFLAGS,System.String,System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "4#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectContainerNode.#SaveItem(Microsoft.VisualStudio.Shell.Interop.VSSAVEFLAGS,System.String,System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectContainerNode.#SaveItem(Microsoft.VisualStudio.Shell.Interop.VSSAVEFLAGS,System.String,System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectContainerNode.#IsItemDirty(System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ProjectContainerNode.#IsItemDirty(System.UInt32,System.IntPtr,System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OASolutionFolder`1.#AddFromTemplate(System.String,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OASolutionFolder`1.#AddFromTemplate(System.String,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OASolutionFolder`1.#AddFromTemplate(System.String,System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OASolutionFolder`1.#AddFromFile(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAReferenceItem.#Open(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAProject.#SaveAs(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAProject.#Save(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAFileItem.#SaveAs(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.Automation.OAFileItem.#Open(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectBuildDependency.#get_Type(System.Guid&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectBuildDependency.#get_ReferredProject(System.Object&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectBuildDependency.#get_MustUpdateBefore(System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectBuildDependency.#get_HelpFile(System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.NestedProjectBuildDependency.#get_Description(System.String&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "5#", Scope = "member", Target = "Microsoft.VisualStudio.Project.FileDocumentManager.#OpenWithSpecific(System.UInt32,System.Guid&,System.String,System.Guid&,System.IntPtr,Microsoft.VisualStudio.Shell.Interop.IVsWindowFrame&,Microsoft.VisualStudio.Project.WindowFrameShowAction)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.EnumDependencies.#Skip(System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.EnumDependencies.#Next(System.UInt32,Microsoft.VisualStudio.Shell.Interop.IVsDependency[],System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.EnumDependencies.#Next(System.UInt32,Microsoft.VisualStudio.Shell.Interop.IVsDependency[],System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.EnumDependencies.#Next(System.UInt32,Microsoft.VisualStudio.Shell.Interop.IVsDependency[],System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.EnumDependencies.#Clone(Microsoft.VisualStudio.Shell.Interop.IVsEnumDependencies&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#UnadviseCfgProviderEvents(System.UInt32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#RenameCfgsOfCfgName(System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#RenameCfgsOfCfgName(System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#OpenProjectCfg(System.String,Microsoft.VisualStudio.Shell.Interop.IVsProjectCfg&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#OpenProjectCfg(System.String,Microsoft.VisualStudio.Shell.Interop.IVsProjectCfg&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#GetSupportedPlatformNames(System.UInt32,System.String[],System.UInt32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#GetSupportedPlatformNames(System.UInt32,System.String[],System.UInt32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#GetPlatformNames(System.UInt32,System.String[],System.UInt32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#GetPlatformNames(System.UInt32,System.String[],System.UInt32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "3#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#GetCfgs(System.UInt32,Microsoft.VisualStudio.Shell.Interop.IVsCfg[],System.UInt32[],System.UInt32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#GetCfgs(System.UInt32,Microsoft.VisualStudio.Shell.Interop.IVsCfg[],System.UInt32[],System.UInt32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#GetCfgs(System.UInt32,Microsoft.VisualStudio.Shell.Interop.IVsCfg[],System.UInt32[],System.UInt32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#GetCfgProviderProperty(System.Int32,System.Object&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#GetCfgOfName(System.String,System.String,Microsoft.VisualStudio.Shell.Interop.IVsCfg&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#GetCfgOfName(System.String,System.String,Microsoft.VisualStudio.Shell.Interop.IVsCfg&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#GetCfgOfName(System.String,System.String,Microsoft.VisualStudio.Shell.Interop.IVsCfg&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#GetCfgNames(System.UInt32,System.String[],System.UInt32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "2#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#GetCfgNames(System.UInt32,System.String[],System.UInt32[])")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#GetAutomationObject(System.String,System.Object&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#GetAutomationObject(System.String,System.Object&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#get_UsesIndependentConfigurations(System.Int32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#DeleteCfgsOfPlatformName(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#DeleteCfgsOfCfgName(System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#AdviseCfgProviderEvents(Microsoft.VisualStudio.Shell.Interop.IVsCfgProviderEvents,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#AdviseCfgProviderEvents(Microsoft.VisualStudio.Shell.Interop.IVsCfgProviderEvents,System.UInt32&)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#AddCfgsOfPlatformName(System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#AddCfgsOfPlatformName(System.String,System.String)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "1#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#AddCfgsOfCfgName(System.String,System.String,System.Int32)")] +[assembly: System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Naming", "CA1725:ParameterNamesShouldMatchBaseDeclaration", MessageId = "0#", Scope = "member", Target = "Microsoft.VisualStudio.Project.ConfigProvider.#AddCfgsOfCfgName(System.String,System.String,System.Int32)")] diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/HierarchyNode.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/HierarchyNode.cs new file mode 100644 index 0000000000..88a0062bac --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/HierarchyNode.cs @@ -0,0 +1,3203 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +//#define CCI_TRACING +using Microsoft.VisualStudio.Shell.Interop; +using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants; +using ShellConstants = Microsoft.VisualStudio.Shell.Interop.Constants; +using VsCommands = Microsoft.VisualStudio.VSConstants.VSStd97CmdID; +using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// An object that deals with user interaction via a GUI in the form a hierarchy: a parent node with zero or more child nodes, each of which + /// can itself be a hierarchy. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling"), CLSCompliant(false), ComVisible(true)] + public abstract class HierarchyNode : + IVsUIHierarchy, + IVsPersistHierarchyItem2, + Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget, + IVsHierarchyDropDataSource2, + IVsHierarchyDropDataSource, + IVsHierarchyDropDataTarget, + IVsHierarchyDeleteHandler, + IDisposable + //, IVsBuildStatusCallback + { + #region nested types + /// + /// DropEffect as defined in oleidl.h + /// + internal enum DropEffect + { + None, + Copy = 1, + Move = 2, + Link = 4 + }; + #endregion + + #region Events + internal event EventHandler OnChildAdded + { + add { onChildAdded += value; } + remove { onChildAdded -= value; } + } + internal event EventHandler OnChildRemoved + { + add { onChildRemoved += value; } + remove { onChildRemoved -= value; } + } + #endregion + + #region static/const fields + public static readonly Guid SolutionExplorer = new Guid(EnvDTE.Constants.vsWindowKindSolutionExplorer); + public const int NoImage = -1; +#if DEBUG + internal static int LastTracedProperty; +#endif + #endregion + + #region fields + private EventSinkCollection hierarchyEventSinks = new EventSinkCollection(); + private ProjectNode projectMgr; + private ProjectElement itemNode; + private HierarchyNode parentNode; + private HierarchyNode nextSibling; + private HierarchyNode firstChild; + private HierarchyNode lastChild; + private bool isExpanded; + private uint hierarchyId; + private uint docCookie; + private bool hasDesigner; + private string virtualNodeName = String.Empty; // Only used by virtual nodes + private IVsHierarchy parentHierarchy; + private int parentHierarchyItemId; + private NodeProperties nodeProperties; + private OleServiceProvider oleServiceProvider = new OleServiceProvider(); + private bool excludeNodeFromScc; + private EventHandler onChildAdded; + private EventHandler onChildRemoved; + private bool hasParentNodeNameRelation; + private List itemsDraggedOrCutOrCopied; + private bool sourceDraggedOrCutOrCopied; + + /// + /// Has the object been disposed. + /// + /// We will not specify a property for isDisposed, rather it is expected that the a private flag is defined + /// on all subclasses. We do not want get in a situation where the base class's dipose is not called because a child sets the flag through the property. + private bool isDisposed; + #endregion + + #region abstract properties + /// + /// The URL of the node. + /// + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1056:UriPropertiesShouldNotBeStrings")] + public abstract string Url + { + get; + } + + /// + /// The Caption of the node. + /// + /// + public abstract string Caption + { + get; + } + + /// + /// The item type guid associated to a node. + /// + /// + public abstract Guid ItemTypeGuid + { + get; + } + #endregion + + #region virtual properties + /// + /// Defines a string that is used to separate the name relation from the extension + /// + public virtual string NameRelationSeparator + { + get + { + return "."; + } + } + + + public virtual int MenuCommandId + { + get { return VsMenus.IDM_VS_CTXT_NOCOMMANDS; } + } + + + /// + /// Return an imageindex + /// + /// + public virtual int ImageIndex + { + get { return NoImage; } + } + + /// + /// Return an state icon index + /// + /// + /// + /// Sets the state icon for a file. + /// + public virtual VsStateIcon StateIconIndex + { + get + { + if(!this.ExcludeNodeFromScc) + { + IVsSccManager2 sccManager = this.ProjectMgr.Site.GetService(typeof(SVsSccManager)) as IVsSccManager2; + + if(sccManager != null) + { + VsStateIcon[] statIcons = new VsStateIcon[1] { VsStateIcon.STATEICON_NOSTATEICON }; + uint[] sccStatus = new uint[1] { 0 }; + // Get the glyph from the scc manager. Note that it will fail in command line + // scenarios. + if(ErrorHandler.Succeeded(sccManager.GetSccGlyph(1, new string[] { this.GetMkDocument() }, statIcons, sccStatus))) + { + return statIcons[0]; + } + } + } + + return VsStateIcon.STATEICON_NOSTATEICON; + } + } + + /// + /// Defines whether a node can execute a command if in selection. + /// + public virtual bool CanExecuteCommand + { + get + { + return true; + } + } + + /// + /// Used to determine the sort order of different node types + /// in the solution explorer window. + /// Nodes with the same priorities are sorted based on their captions. + /// + public virtual int SortPriority + { + get { return DefaultSortOrderNode.HierarchyNode; } + } + + /// + /// Defines the properties attached to this node. + /// + public virtual NodeProperties NodeProperties + { + get + { + if(null == nodeProperties) + { + nodeProperties = CreatePropertiesObject(); + } + return this.nodeProperties; + } + + } + + /// + /// Returns an object that is a special view over this object; this is the value + /// returned by the Object property of the automation objects. + /// + internal virtual object Object + { + get { return this; } + } + #endregion + + #region properties + + public OleServiceProvider OleServiceProvider + { + get + { + return this.oleServiceProvider; + } + } + + [System.ComponentModel.BrowsableAttribute(false)] + public ProjectNode ProjectMgr + { + get + { + return this.projectMgr; + } + set + { + this.projectMgr = value; + } + } + + + [System.ComponentModel.BrowsableAttribute(false)] + public HierarchyNode NextSibling + { + get + { + return this.nextSibling; + } + set + { + this.nextSibling = value; + } + } + + + [System.ComponentModel.BrowsableAttribute(false)] + public HierarchyNode FirstChild + { + get + { + return this.firstChild; + } + set + { + this.firstChild = value; + } + } + + [System.ComponentModel.BrowsableAttribute(false)] + public HierarchyNode LastChild + { + get + { + return this.lastChild; + } + set + { + this.lastChild = value; + } + } + + + [System.ComponentModel.BrowsableAttribute(false)] + public HierarchyNode Parent + { + get + { + return this.parentNode; + } + set + { + this.parentNode = value; + } + } + + + [System.ComponentModel.BrowsableAttribute(false)] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID")] + public uint ID + { + get + { + return this.hierarchyId; + } + internal set + { + this.hierarchyId = value; + } + } + + + [System.ComponentModel.BrowsableAttribute(false)] + public ProjectElement ItemNode + { + get + { + return itemNode; + } + set + { + itemNode = value; + } + } + + + [System.ComponentModel.BrowsableAttribute(false)] + public bool HasDesigner + { + get + { + return this.hasDesigner; + } + set { this.hasDesigner = value; } + } + + + [System.ComponentModel.BrowsableAttribute(false)] + public bool IsExpanded + { + get + { + return this.isExpanded; + } + set { this.isExpanded = value; } + } + + public string VirtualNodeName + { + get + { + return this.virtualNodeName; + } + set + { + this.virtualNodeName = value; + } + } + + + [System.ComponentModel.BrowsableAttribute(false)] + public HierarchyNode PreviousSibling + { + get + { + if(this.parentNode == null) return null; + HierarchyNode prev = null; + for(HierarchyNode child = this.parentNode.firstChild; child != null; child = child.nextSibling) + { + if(child == this) + break; + prev = child; + } + return prev; + } + } + + public uint DocCookie + { + get + { + return this.docCookie; + } + set + { + this.docCookie = value; + } + } + + /// + /// Specifies if a Node is under source control. + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")] + public bool ExcludeNodeFromScc + { + get + { + return this.excludeNodeFromScc; + } + set + { + this.excludeNodeFromScc = value; + } + } + + /// + /// Defines if a node a name relation to its parent node + /// + /// + public bool HasParentNodeNameRelation + { + get + { + return this.hasParentNodeNameRelation; + } + set + { + this.hasParentNodeNameRelation = value; + } + } + + protected bool SourceDraggedOrCutOrCopied + { + get + { + return this.sourceDraggedOrCutOrCopied; + } + set + { + this.sourceDraggedOrCutOrCopied = value; + } + } + + protected IList ItemsDraggedOrCutOrCopied + { + get + { + return this.itemsDraggedOrCutOrCopied; + } + } + #endregion + + #region ctors + + protected HierarchyNode() + { + this.IsExpanded = true; + } + + protected HierarchyNode(ProjectNode root, ProjectElement element) + { + if (root == null) + { + throw new ArgumentNullException("root"); + } + + this.projectMgr = root; + this.itemNode = element; + this.hierarchyId = this.projectMgr.ItemIdMap.Add(this); + this.oleServiceProvider.AddService(typeof(IVsHierarchy), root, false); + } + + /// + /// Overloaded ctor. + /// + /// + protected HierarchyNode(ProjectNode root) + { + if (root == null) + { + throw new ArgumentNullException("root"); + } + + this.projectMgr = root; + this.itemNode = new ProjectElement(this.projectMgr, null, true); + this.hierarchyId = this.projectMgr.ItemIdMap.Add(this); + this.oleServiceProvider.AddService(typeof(IVsHierarchy), root, false); + } + #endregion + + #region static methods + /// + /// Get the outer IVsHierarchy implementation. + /// This is used for scenario where a flavor may be modifying the behavior + /// + internal static IVsHierarchy GetOuterHierarchy(HierarchyNode node) + { + IVsHierarchy hierarchy = null; + // The hierarchy of a node is its project node hierarchy + IntPtr projectUnknown = Marshal.GetIUnknownForObject(node.projectMgr); + try + { + hierarchy = (IVsHierarchy)Marshal.GetTypedObjectForIUnknown(projectUnknown, typeof(IVsHierarchy)); + } + finally + { + if(projectUnknown != IntPtr.Zero) + { + Marshal.Release(projectUnknown); + } + } + return hierarchy; + } + #endregion + + #region virtual methods + /// + /// Creates an object derived from NodeProperties that will be used to expose properties + /// spacific for this object to the property browser. + /// + /// + protected virtual NodeProperties CreatePropertiesObject() + { + return null; + } + + /// + /// Return an iconhandle + /// + /// + /// + public virtual object GetIconHandle(bool open) + { + return null; + } + + /// + /// AddChild - add a node, sorted in the right location. + /// + /// The node to add. + public virtual void AddChild(HierarchyNode node) + { + if(node == null) + { + throw new ArgumentNullException("node"); + } + + // make sure the node is in the map. + Object nodeWithSameID = this.projectMgr.ItemIdMap[node.hierarchyId]; + if(!Object.ReferenceEquals(node, nodeWithSameID as HierarchyNode)) + { + if(nodeWithSameID == null && node.ID <= this.ProjectMgr.ItemIdMap.Count) + { // reuse our hierarchy id if possible. + this.projectMgr.ItemIdMap.SetAt(node.hierarchyId, this); + } + else + { + throw new InvalidOperationException(); + } + } + + HierarchyNode previous = null; + for(HierarchyNode n = this.firstChild; n != null; n = n.nextSibling) + { + if(this.ProjectMgr.CompareNodes(node, n) > 0) break; + previous = n; + } + // insert "node" after "previous". + if(previous != null) + { + node.nextSibling = previous.nextSibling; + previous.nextSibling = node; + if(previous == this.lastChild) + { + this.lastChild = node; + } + } + else + { + if(this.lastChild == null) + { + this.lastChild = node; + } + node.nextSibling = this.firstChild; + this.firstChild = node; + } + node.parentNode = this; + this.OnItemAdded(this, node); + } + + /// + /// Removes a node from the hierarchy. + /// + /// The node to remove. + public virtual void RemoveChild(HierarchyNode node) + { + if(node == null) + { + throw new ArgumentNullException("node"); + } + + this.projectMgr.ItemIdMap.Remove(node); + + HierarchyNode last = null; + for(HierarchyNode n = this.firstChild; n != null; n = n.nextSibling) + { + if(n == node) + { + if(last != null) + { + last.nextSibling = n.nextSibling; + } + if(n == this.lastChild) + { + if(last == this.lastChild) + { + this.lastChild = null; + } + else + { + this.lastChild = last; + } + } + if(n == this.firstChild) + { + this.firstChild = n.nextSibling; + } + return; + } + last = n; + } + throw new InvalidOperationException("Node not found"); + } + + /// + /// Returns an automation object representing this node + /// + /// The automation object + public virtual object GetAutomationObject() + { + return new Automation.OAProjectItem(this.projectMgr.GetAutomationObject() as Automation.OAProject, this); + } + + /// + /// Returns a property object based on a property id + /// + /// the property id of the property requested + /// the property object requested + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + public virtual object GetProperty(int propId) + { + object result = null; + switch((__VSHPROPID)propId) + { + case __VSHPROPID.VSHPROPID_Expandable: + result = (this.firstChild != null); + break; + + case __VSHPROPID.VSHPROPID_Caption: + result = this.Caption; + break; + + case __VSHPROPID.VSHPROPID_Name: + result = this.Caption; + break; + + case __VSHPROPID.VSHPROPID_ExpandByDefault: + result = false; + break; + + case __VSHPROPID.VSHPROPID_IconImgList: + result = this.ProjectMgr.ImageHandler.ImageList.Handle; + break; + + case __VSHPROPID.VSHPROPID_OpenFolderIconIndex: + case __VSHPROPID.VSHPROPID_IconIndex: + int index = this.ImageIndex; + if(index != NoImage) + { + result = index; + } + break; + + case __VSHPROPID.VSHPROPID_StateIconIndex: + result = (int)this.StateIconIndex; + break; + + case __VSHPROPID.VSHPROPID_IconHandle: + result = GetIconHandle(false); + break; + + case __VSHPROPID.VSHPROPID_OpenFolderIconHandle: + result = GetIconHandle(true); + break; + + case __VSHPROPID.VSHPROPID_NextVisibleSibling: + goto case __VSHPROPID.VSHPROPID_NextSibling; + + case __VSHPROPID.VSHPROPID_NextSibling: + result = (int)((this.nextSibling != null) ? this.nextSibling.hierarchyId : VSConstants.VSITEMID_NIL); + break; + + case __VSHPROPID.VSHPROPID_FirstChild: + goto case __VSHPROPID.VSHPROPID_FirstVisibleChild; + + case __VSHPROPID.VSHPROPID_FirstVisibleChild: + result = (int)((this.firstChild != null) ? this.firstChild.hierarchyId : VSConstants.VSITEMID_NIL); + break; + + case __VSHPROPID.VSHPROPID_Parent: + if(null == this.parentNode) + { + unchecked { result = new IntPtr((int)VSConstants.VSITEMID_NIL); } + } + else + { + result = new IntPtr((int)this.parentNode.hierarchyId); // see bug 176470 + } + break; + + case __VSHPROPID.VSHPROPID_ParentHierarchyItemid: + if(parentHierarchy != null) + { + result = (IntPtr)parentHierarchyItemId; // VS requires VT_I4 | VT_INT_PTR + } + break; + + case __VSHPROPID.VSHPROPID_ParentHierarchy: + result = parentHierarchy; + break; + + case __VSHPROPID.VSHPROPID_Root: + result = Marshal.GetIUnknownForObject(this.projectMgr); + break; + + case __VSHPROPID.VSHPROPID_Expanded: + result = this.isExpanded; + break; + + case __VSHPROPID.VSHPROPID_BrowseObject: + result = this.NodeProperties; + if(result != null) result = new DispatchWrapper(result); + break; + + case __VSHPROPID.VSHPROPID_EditLabel: + if(this.ProjectMgr != null && !this.ProjectMgr.IsClosed && !this.ProjectMgr.IsCurrentStateASuppressCommandsMode()) + { + result = GetEditLabel(); + } + break; + + case __VSHPROPID.VSHPROPID_SaveName: + //SaveName is the name shown in the Save and the Save Changes dialog boxes. + result = this.Caption; + break; + + case __VSHPROPID.VSHPROPID_ItemDocCookie: + if(this.docCookie != 0) return (IntPtr)this.docCookie; //cast to IntPtr as some callers expect VT_INT + break; + + case __VSHPROPID.VSHPROPID_ExtObject: + result = GetAutomationObject(); + break; + } + + __VSHPROPID2 id2 = (__VSHPROPID2)propId; + switch(id2) + { + case __VSHPROPID2.VSHPROPID_NoDefaultNestedHierSorting: + return true; // We are doing the sorting ourselves through VSHPROPID_FirstChild and VSHPROPID_NextSibling + case __VSHPROPID2.VSHPROPID_BrowseObjectCATID: + { + // If there is a browse object and it is a NodeProperties, then get it's CATID + object browseObject = this.GetProperty((int)__VSHPROPID.VSHPROPID_BrowseObject); + if(browseObject != null) + { + if(browseObject is DispatchWrapper) + browseObject = ((DispatchWrapper)browseObject).WrappedObject; + result = this.ProjectMgr.GetCATIDForType(browseObject.GetType()).ToString("B"); + if(String.CompareOrdinal(result as string, Guid.Empty.ToString("B")) == 0) + result = null; + } + break; + } + case __VSHPROPID2.VSHPROPID_ExtObjectCATID: + { + // If there is a extensibility object and it is a NodeProperties, then get it's CATID + object extObject = this.GetProperty((int)__VSHPROPID.VSHPROPID_ExtObject); + if(extObject != null) + { + if(extObject is DispatchWrapper) + extObject = ((DispatchWrapper)extObject).WrappedObject; + result = this.ProjectMgr.GetCATIDForType(extObject.GetType()).ToString("B"); + if(String.CompareOrdinal(result as string, Guid.Empty.ToString("B")) == 0) + result = null; + } + break; + } + } +#if DEBUG + if(propId != LastTracedProperty) + { + string trailer = (result == null) ? "null" : result.ToString(); + CCITracing.TraceCall(this.hierarchyId + "," + propId.ToString() + " = " + trailer); + LastTracedProperty = propId; // some basic filtering here... + } +#endif + return result; + } + + /// + /// Sets the value of a property for a given property id + /// + /// the property id of the property to be set + /// value of the property + /// S_OK if succeeded + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "propid")] + public virtual int SetProperty(int propid, object value) + { + __VSHPROPID id = (__VSHPROPID)propid; + + CCITracing.TraceCall(this.hierarchyId + "," + id.ToString()); + switch(id) + { + case __VSHPROPID.VSHPROPID_Expanded: + this.isExpanded = (bool)value; + break; + + case __VSHPROPID.VSHPROPID_ParentHierarchy: + parentHierarchy = (IVsHierarchy)value; + break; + + case __VSHPROPID.VSHPROPID_ParentHierarchyItemid: + parentHierarchyItemId = (int)value; + break; + + case __VSHPROPID.VSHPROPID_EditLabel: + return SetEditLabel((string)value); + + default: + CCITracing.TraceCall(" unhandled"); + break; + } + return VSConstants.S_OK; + } + + /// + /// Get a guid property + /// + /// property id for the guid property requested + /// the requested guid + /// S_OK if succeded + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "propid")] + public virtual int GetGuidProperty(int propid, out Guid guid) + { + guid = Guid.Empty; + if(propid == (int)__VSHPROPID.VSHPROPID_TypeGuid) + { + guid = this.ItemTypeGuid; + } + + if(guid.CompareTo(Guid.Empty) == 0) + { + return VSConstants.DISP_E_MEMBERNOTFOUND; + } + + return VSConstants.S_OK; + } + + /// + /// Set a guid property. + /// + /// property id of the guid property to be set + /// the guid to be set + /// E_NOTIMPL + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "propid")] + public virtual int SetGuidProperty(int propid, ref Guid guid) + { + return VSConstants.E_NOTIMPL; + } + + /// + /// Called by the shell when a node has been renamed from the GUI + /// + /// + /// E_NOTIMPL + public virtual int SetEditLabel(string label) + { + return VSConstants.E_NOTIMPL; + } + + /// + /// Called by the shell to get the node caption when the user tries to rename from the GUI + /// + /// the node cation + public virtual string GetEditLabel() + { + return this.Caption; + } + + /// + /// This method is called by the interface method GetMkDocument to specify the item moniker. + /// + /// The moniker for this item + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Mk")] + public virtual string GetMkDocument() + { + return String.Empty; + } + + /// + /// Removes items from the hierarchy. Project overwrites this + /// + /// + public virtual void Remove(bool removeFromStorage) + { + string documentToRemove = this.GetMkDocument(); + + // Ask Document tracker listeners if we can remove the item. + string[] filesToBeDeleted = new string[1] { documentToRemove }; + VSQUERYREMOVEFILEFLAGS[] queryRemoveFlags = this.GetQueryRemoveFileFlags(filesToBeDeleted); + if(!this.ProjectMgr.Tracker.CanRemoveItems(filesToBeDeleted, queryRemoveFlags)) + { + return; + } + + // Close the document if it has a manager. + DocumentManager manager = this.GetDocumentManager(); + if(manager != null) + { + if(manager.Close(!removeFromStorage ? __FRAMECLOSE.FRAMECLOSE_PromptSave : __FRAMECLOSE.FRAMECLOSE_NoSave) == VSConstants.E_ABORT) + { + // User cancelled operation in message box. + return; + } + } + + // Check out the project file. + if(!this.ProjectMgr.QueryEditProjectFile(false)) + { + throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + + // Notify hierarchy event listeners that the file is going to be removed. + OnItemDeleted(); + + // Remove child if any before removing from the hierarchy + for(HierarchyNode child = this.FirstChild; child != null; child = child.NextSibling) + { + child.Remove(removeFromStorage); + } + + // the project node has no parentNode + if(this.parentNode != null) + { + // Remove from the Hierarchy + this.parentNode.RemoveChild(this); + } + + // We save here the path to delete since this.Url might call the Include which will be deleted by the RemoveFromProjectFile call. + string pathToDelete = this.GetMkDocument(); + this.itemNode.RemoveFromProjectFile(); + + if(removeFromStorage) + { + this.DeleteFromStorage(pathToDelete); + } + + // Close the document window if opened. + CloseDocumentWindow(this); + + // Notify document tracker listeners that we have removed the item. + VSREMOVEFILEFLAGS[] removeFlags = this.GetRemoveFileFlags(filesToBeDeleted); + Debug.Assert(removeFlags != null, "At least an empty array should be returned for the GetRemoveFileFlags"); + this.ProjectMgr.Tracker.OnItemRemoved(documentToRemove, removeFlags[0]); + + // Notify hierarchy event listeners that we have removed the item + if(null != this.parentNode.onChildRemoved) + { + HierarchyNodeEventArgs args = new HierarchyNodeEventArgs(this); + parentNode.onChildRemoved(parentNode, args); + } + + // Notify hierarchy event listeners that items have been invalidated + OnInvalidateItems(this.parentNode); + + // Dispose the node now that is deleted. + this.Dispose(true); + } + + /// + /// Returns the relational name which is defined as the first part of the caption until indexof NameRelationSeparator + /// + public virtual string GetRelationalName() + { + //Get the first part of the caption + string[] partsOfParent = this.Caption.Split(new string[] { this.NameRelationSeparator }, StringSplitOptions.None); + return partsOfParent[0]; + } + + /// + /// Returns the 'extension' of the relational name + /// e.g. form1.resx returns .resx, form1.designer.cs returns .designer.cs + /// + /// The extension + public virtual string GetRelationNameExtension() + { + return this.Caption.Substring(this.Caption.IndexOf(this.NameRelationSeparator, StringComparison.Ordinal)); + } + + /// + /// Close open document frame for a specific node. + /// + protected void CloseDocumentWindow(HierarchyNode node) + { + if (node == null) + { + throw new ArgumentNullException("node"); + } + + // We walk the RDT looking for all running documents attached to this hierarchy and itemid. There + // are cases where there may be two different editors (not views) open on the same document. + IEnumRunningDocuments pEnumRdt; + IVsRunningDocumentTable pRdt = this.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + if(pRdt == null) + { + throw new InvalidOperationException(); + } + if(ErrorHandler.Succeeded(pRdt.GetRunningDocumentsEnum(out pEnumRdt))) + { + uint[] cookie = new uint[1]; + uint fetched; + uint saveOptions = (uint)__VSSLNSAVEOPTIONS.SLNSAVEOPT_NoSave; + IVsHierarchy srpOurHier = node.projectMgr as IVsHierarchy; + + ErrorHandler.ThrowOnFailure(pEnumRdt.Reset()); + while(VSConstants.S_OK == pEnumRdt.Next(1, cookie, out fetched)) + { + // Note we can pass NULL for all parameters we don't care about + uint empty; + string emptyStr; + IntPtr ppunkDocData; + IVsHierarchy srpHier; + uint itemid = VSConstants.VSITEMID_NIL; + + ErrorHandler.ThrowOnFailure(pRdt.GetDocumentInfo( + cookie[0], + out empty, + out empty, + out empty, + out emptyStr, + out srpHier, + out itemid, + out ppunkDocData)); + + // Is this one of our documents? + if(Utilities.IsSameComObject(srpOurHier, srpHier) && itemid == node.ID) + { + IVsSolution soln = GetService(typeof(SVsSolution)) as IVsSolution; + ErrorHandler.ThrowOnFailure(soln.CloseSolutionElement(saveOptions, srpOurHier, cookie[0])); + } + if(ppunkDocData != IntPtr.Zero) + Marshal.Release(ppunkDocData); + + } + } + } + + /// + /// Redraws the state icon if the node is not excluded from source control. + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")] + protected internal virtual void UpdateSccStateIcons() + { + if(!this.ExcludeNodeFromScc) + { + this.ReDraw(UIHierarchyElement.SccState); + } + } + + /// + /// To be overwritten by descendants. + /// + protected internal virtual int SetEditLabel(string label, string relativePath) + { + throw new NotImplementedException(); + } + + /// + /// Called by the drag and drop implementation to ask the node + /// which is being dragged/droped over which nodes should + /// process the operation. + /// This allows for dragging to a node that cannot contain + /// items to let its parent accept the drop + /// + /// HierarchyNode that accept the drop handling + protected internal virtual HierarchyNode GetDragTargetHandlerNode() + { + return this; + } + + /// + /// Add a new Folder to the project hierarchy. + /// + /// S_OK if succeeded, otherwise an error + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + protected virtual int AddNewFolder() + { + // Check out the project file. + if(!this.ProjectMgr.QueryEditProjectFile(false)) + { + throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + + try + { + // Generate a new folder name + string newFolderName; + ErrorHandler.ThrowOnFailure(this.projectMgr.GenerateUniqueItemName(this.hierarchyId, String.Empty, String.Empty, out newFolderName)); + + // create the project part of it, the project file + HierarchyNode child = this.ProjectMgr.CreateFolderNodes(Path.Combine(this.virtualNodeName, newFolderName)); + + if(child is FolderNode) + { + ((FolderNode)child).CreateDirectory(); + } + + // If we are in automation mode then skip the ui part which is about renaming the folder + if(!Utilities.IsInAutomationFunction(this.projectMgr.Site)) + { + IVsUIHierarchyWindow uiWindow = UIHierarchyUtilities.GetUIHierarchyWindow(this.projectMgr.Site, SolutionExplorer); + // we need to get into label edit mode now... + // so first select the new guy... + ErrorHandler.ThrowOnFailure(uiWindow.ExpandItem(this.projectMgr, child.hierarchyId, EXPANDFLAGS.EXPF_SelectItem)); + // them post the rename command to the shell. Folder verification and creation will + // happen in the setlabel code... + IVsUIShell shell = this.projectMgr.Site.GetService(typeof(SVsUIShell)) as IVsUIShell; + + Debug.Assert(shell != null, "Could not get the ui shell from the project"); + if(shell == null) + { + return VSConstants.E_FAIL; + } + + object dummy = null; + Guid cmdGroup = VsMenus.guidStandardCommandSet97; + ErrorHandler.ThrowOnFailure(shell.PostExecCommand(ref cmdGroup, (uint)VsCommands.Rename, 0, ref dummy)); + } + } + catch(COMException e) + { + Trace.WriteLine("Exception : " + e.Message); + return e.ErrorCode; + } + + return VSConstants.S_OK; + } + + protected virtual int AddItemToHierarchy(HierarchyAddType addType) + { + CCITracing.TraceCall(); + IVsAddProjectItemDlg addItemDialog; + + string strFilter = String.Empty; + int iDontShowAgain; + uint uiFlags; + IVsProject3 project = (IVsProject3)this.projectMgr; + + string strBrowseLocations = Path.GetDirectoryName(this.projectMgr.BaseURI.Uri.LocalPath); + + System.Guid projectGuid = this.projectMgr.ProjectGuid; + + addItemDialog = this.GetService(typeof(IVsAddProjectItemDlg)) as IVsAddProjectItemDlg; + + if(addType == HierarchyAddType.AddNewItem) + uiFlags = (uint)(__VSADDITEMFLAGS.VSADDITEM_AddNewItems | __VSADDITEMFLAGS.VSADDITEM_SuggestTemplateName | __VSADDITEMFLAGS.VSADDITEM_AllowHiddenTreeView); + else + uiFlags = (uint)(__VSADDITEMFLAGS.VSADDITEM_AddExistingItems | __VSADDITEMFLAGS.VSADDITEM_AllowMultiSelect | __VSADDITEMFLAGS.VSADDITEM_AllowStickyFilter); + + ErrorHandler.ThrowOnFailure(addItemDialog.AddProjectItemDlg(this.hierarchyId, ref projectGuid, project, uiFlags, null, null, ref strBrowseLocations, ref strFilter, out iDontShowAgain)); /*&fDontShowAgain*/ + + return VSConstants.S_OK; + } + + /// + /// Overwritten in subclasses + /// + protected virtual void DoDefaultAction() + { + CCITracing.TraceCall(); + } + + /// + /// Handles the exclude from project command. + /// + /// + protected virtual int ExcludeFromProject() + { + Debug.Assert(this.ProjectMgr != null, "The project item " + this.ToString() + " has not been initialised correctly. It has a null ProjectMgr"); + this.Remove(false); + return VSConstants.S_OK; + } + + /// + /// Handles the Show in Designer command. + /// + /// + protected virtual int ShowInDesigner(IList selectedNodes) + { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + /// + /// Prepares a selected node for clipboard. + /// It takes the the project reference string of this item and adds it to a stringbuilder. + /// + /// A stringbuilder. + /// This method has to be public since seleceted nodes will call it. + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ClipBoard")] + protected internal virtual StringBuilder PrepareSelectedNodesForClipBoard() + { + Debug.Assert(this.ProjectMgr != null, " No project mananager available for this node " + ToString()); + Debug.Assert(this.ProjectMgr.ItemsDraggedOrCutOrCopied != null, " The itemsdragged list should have been initialized prior calling this method"); + StringBuilder sb = new StringBuilder(); + + if(this.hierarchyId == VSConstants.VSITEMID_ROOT) + { + if(this.ProjectMgr.ItemsDraggedOrCutOrCopied != null) + { + this.ProjectMgr.ItemsDraggedOrCutOrCopied.Clear();// abort + } + return sb; + } + + if(this.ProjectMgr.ItemsDraggedOrCutOrCopied != null) + { + this.ProjectMgr.ItemsDraggedOrCutOrCopied.Add(this); + } + + string projref = String.Empty; + IVsSolution solution = this.GetService(typeof(IVsSolution)) as IVsSolution; + if(solution != null) + { + ErrorHandler.ThrowOnFailure(solution.GetProjrefOfItem(this.ProjectMgr, this.hierarchyId, out projref)); + if(String.IsNullOrEmpty(projref)) + { + if(this.ProjectMgr.ItemsDraggedOrCutOrCopied != null) + { + this.ProjectMgr.ItemsDraggedOrCutOrCopied.Clear();// abort + } + return sb; + } + } + + // Append the projectref and a null terminator to the string builder + sb.Append(projref); + sb.Append('\0'); + + return sb; + } + + /// + /// Returns the Cannonical Name + /// + /// Cannonical Name + protected virtual string GetCanonicalName() + { + return this.GetMkDocument(); + } + + /// + /// Factory method for the Document Manager object + /// + /// null object, since a hierarchy node does not know its kind of document + /// Must be overriden by derived node classes if a document manager is needed + protected internal virtual DocumentManager GetDocumentManager() + { + return null; + } + + /// + /// Displays the context menu. + /// + /// list of selected nodes. + /// contains the location (x,y) at which to show the menu. + [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "pointer")] + protected virtual int DisplayContextMenu(IList selectedNodes, IntPtr pointerToVariant) + { + if(selectedNodes == null || selectedNodes.Count == 0 || pointerToVariant == IntPtr.Zero) + { + return NativeMethods.OLECMDERR_E_NOTSUPPORTED; + } + + int idmxStoredMenu = 0; + + foreach(HierarchyNode node in selectedNodes) + { + // We check here whether we have a multiple selection of + // nodes of differing type. + if(idmxStoredMenu == 0) + { + // First time through or single node case + idmxStoredMenu = node.MenuCommandId; + } + else if(idmxStoredMenu != node.MenuCommandId) + { + // We have different node types. Check if any of the nodes is + // the project node and set the menu accordingly. + if(node.MenuCommandId == VsMenus.IDM_VS_CTXT_PROJNODE) + { + idmxStoredMenu = VsMenus.IDM_VS_CTXT_XPROJ_PROJITEM; + } + else + { + idmxStoredMenu = VsMenus.IDM_VS_CTXT_XPROJ_MULTIITEM; + } + } + } + + object variant = Marshal.GetObjectForNativeVariant(pointerToVariant); + UInt32 pointsAsUint = (UInt32)variant; + short x = (short)(pointsAsUint & 0x0000ffff); + short y = (short)((pointsAsUint & 0xffff0000) / 0x10000); + + + POINTS points = new POINTS(); + points.x = x; + points.y = y; + return ShowContextMenu(idmxStoredMenu, VsMenus.guidSHLMainMenu, points); + } + + /// + /// Shows the specified context menu at a specified location. + /// + /// The context menu ID. + /// The GUID of the menu group. + /// The location at which to show the menu. + protected virtual int ShowContextMenu(int menuId, Guid menuGroup, POINTS points) + { + IVsUIShell shell = this.projectMgr.Site.GetService(typeof(SVsUIShell)) as IVsUIShell; + + Debug.Assert(shell != null, "Could not get the ui shell from the project"); + if(shell == null) + { + return VSConstants.E_FAIL; + } + POINTS[] pnts = new POINTS[1]; + pnts[0].x = points.x; + pnts[0].y = points.y; + return shell.ShowContextMenu(0, ref menuGroup, menuId, pnts, (Microsoft.VisualStudio.OLE.Interop.IOleCommandTarget)this); + } + + #region initiation of command execution + /// + /// Handles command execution. + /// + /// Unique identifier of the command group + /// The command to be executed. + /// Values describe how the object should execute the command. + /// Pointer to a VARIANTARG structure containing input arguments. Can be NULL + /// VARIANTARG structure to receive command output. Can be NULL. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Cmdexecopt")] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "n")] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "pva")] + protected virtual int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) + { + if(this.projectMgr == null || this.projectMgr.IsClosed) + { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + if(cmdGroup == Guid.Empty) + { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + else if(cmdGroup == VsMenus.guidVsUIHierarchyWindowCmds) + { + switch(cmd) + { + case (uint)VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_DoubleClick: + case (uint)VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_EnterKey: + this.DoDefaultAction(); + return VSConstants.S_OK; + } + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + else if(cmdGroup == VsMenus.guidStandardCommandSet97) + { + HierarchyNode nodeToAddTo = this.GetDragTargetHandlerNode(); + switch((VsCommands)cmd) + { + case VsCommands.AddNewItem: + return nodeToAddTo.AddItemToHierarchy(HierarchyAddType.AddNewItem); + + case VsCommands.AddExistingItem: + return nodeToAddTo.AddItemToHierarchy(HierarchyAddType.AddExistingItem); + + case VsCommands.NewFolder: + return nodeToAddTo.AddNewFolder(); + + case VsCommands.Paste: + return this.ProjectMgr.PasteFromClipboard(this); + } + + } + else if(cmdGroup == VsMenus.guidStandardCommandSet2K) + { + switch((VsCommands2K)cmd) + { + case VsCommands2K.EXCLUDEFROMPROJECT: + return this.ExcludeFromProject(); + } + } + + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + /// + /// Executes a command that can only be executed once the whole selection is known. + /// + /// Unique identifier of the command group + /// The command to be executed. + /// Values describe how the object should execute the command. + /// Pointer to a VARIANTARG structure containing input arguments. Can be NULL + /// VARIANTARG structure to receive command output. Can be NULL. + /// The origin of the command. From IOleCommandTarget or hierarchy. + /// The list of the selected nodes. + /// An out parameter specifying that the command was handled. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "vaIn")] + protected virtual int ExecCommandThatDependsOnSelectedNodes(Guid cmdGroup, uint cmdId, uint cmdExecOpt, IntPtr vaIn, IntPtr vaOut, CommandOrigin commandOrigin, IList selectedNodes, out bool handled) + { + handled = false; + if(cmdGroup == VsMenus.guidVsUIHierarchyWindowCmds) + { + switch(cmdId) + { + case (uint)VSConstants.VsUIHierarchyWindowCmdIds.UIHWCMDID_RightClick: + // The UIHWCMDID_RightClick is what tells an IVsUIHierarchy in a UIHierarchyWindow + // to put up the context menu. Since the mouse may have moved between the + // mouse down and the mouse up, GetCursorPos won't tell you the right place + // to put the context menu (especially if it came through the keyboard). + // So we pack the proper menu position into pvaIn by + // memcpy'ing a POINTS struct into the VT_UI4 part of the pvaIn variant. The + // code to unpack it looks like this: + // ULONG ulPts = V_UI4(pvaIn); + // POINTS pts; + // memcpy((void*)&pts, &ulPts, sizeof(POINTS)); + // You then pass that POINTS into DisplayContextMenu. + handled = true; + return this.DisplayContextMenu(selectedNodes, vaIn); + default: + break; + } + } + else if(cmdGroup == VsMenus.guidStandardCommandSet2K) + { + switch((VsCommands2K)cmdId) + { + case VsCommands2K.ViewInClassDiagram: + handled = true; + return this.ShowInDesigner(selectedNodes); + } + } + + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + /// + /// Executes command that are independent of a selection. + /// + /// Unique identifier of the command group + /// The command to be executed. + /// Values describe how the object should execute the command. + /// Pointer to a VARIANTARG structure containing input arguments. Can be NULL + /// VARIANTARG structure to receive command output. Can be NULL. + /// The origin of the command. From IOleCommandTarget or hierarchy. + /// An out parameter specifying that the command was handled. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "vaIn")] + protected virtual int ExecCommandIndependentOfSelection(Guid cmdGroup, uint cmdId, uint cmdExecOpt, IntPtr vaIn, IntPtr vaOut, CommandOrigin commandOrigin, out bool handled) + { + handled = false; + + if(this.projectMgr == null || this.projectMgr.IsClosed) + { + return VSConstants.E_FAIL; + } + + if(cmdGroup == VsMenus.guidStandardCommandSet97) + { + if(commandOrigin == CommandOrigin.OleCommandTarget) + { + switch((VsCommands)cmdId) + { + case VsCommands.Cut: + case VsCommands.Copy: + case VsCommands.Paste: + case VsCommands.Rename: + handled = true; + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + } + + switch((VsCommands)cmdId) + { + case VsCommands.Copy: + handled = true; + return this.ProjectMgr.CopyToClipboard(); + + case VsCommands.Cut: + handled = true; + return this.ProjectMgr.CutToClipboard(); + + case VsCommands.SolutionCfg: + handled = true; + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + + case VsCommands.SearchCombo: + handled = true; + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + + } + } + else if(cmdGroup == VsMenus.guidStandardCommandSet2K) + { + // There should only be the project node who handles these and should manifest in the same action regardles of selection. + switch((VsCommands2K)cmdId) + { + case VsCommands2K.SHOWALLFILES: + handled = true; + return this.projectMgr.ShowAllFiles(); + case VsCommands2K.ADDREFERENCE: + handled = true; + return this.projectMgr.AddProjectReference(); + case VsCommands2K.ADDWEBREFERENCE: + handled = true; + return this.projectMgr.AddWebReference(); + } + } + + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + /// + /// The main entry point for command excection. Gets called from the IVsUIHierarchy and IOleCommandTarget methods. + /// + /// Unique identifier of the command group + /// The command to be executed. + /// Values describe how the object should execute the command. + /// Pointer to a VARIANTARG structure containing input arguments. Can be NULL + /// VARIANTARG structure to receive command output. Can be NULL. + /// The origin of the command. From IOleCommandTarget or hierarchy. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "vaIn")] + protected virtual int InternalExecCommand(Guid cmdGroup, uint cmdId, uint cmdExecOpt, IntPtr vaIn, IntPtr vaOut, CommandOrigin commandOrigin) + { + CCITracing.TraceCall(cmdGroup.ToString() + "," + cmdId.ToString()); + if(this.projectMgr == null || this.projectMgr.IsClosed) + { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + if(cmdGroup == Guid.Empty) + { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + IList selectedNodes = this.projectMgr.GetSelectedNodes(); + + // Check if all nodes can execute a command. If there is at least one that cannot return not handled. + foreach(HierarchyNode node in selectedNodes) + { + if(!node.CanExecuteCommand) + { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + } + + // Handle commands that are independent of a selection. + bool handled = false; + int returnValue = this.ExecCommandIndependentOfSelection(cmdGroup, cmdId, cmdExecOpt, vaIn, vaOut, commandOrigin, out handled); + if(handled) + { + return returnValue; + } + + + // Now handle commands that need the selected nodes as input parameter. + returnValue = this.ExecCommandThatDependsOnSelectedNodes(cmdGroup, cmdId, cmdExecOpt, vaIn, vaOut, commandOrigin, selectedNodes, out handled); + if(handled) + { + return returnValue; + } + + returnValue = (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + + // Handle commands iteratively. The same action will be executed for all of the selected items. + foreach(HierarchyNode node in selectedNodes) + { + try + { + returnValue = node.ExecCommandOnNode(cmdGroup, cmdId, cmdExecOpt, vaIn, vaOut); + } + catch(COMException e) + { + Trace.WriteLine("Exception : " + e.Message); + returnValue = e.ErrorCode; + } + if(returnValue != VSConstants.S_OK) + { + break; + } + } + + if(returnValue == VSConstants.E_ABORT || returnValue == VSConstants.OLE_E_PROMPTSAVECANCELLED) + { + returnValue = VSConstants.S_OK; + } + + return returnValue; + } + + #endregion + + #region query command handling + /// + /// Handles menus originating from IOleCommandTarget. + /// + /// Unique identifier of the command group + /// The command to be executed. + /// Specifies whether the menu was handled. + /// A QueryStatusResult describing the status of the menu. + protected virtual QueryStatusResult QueryStatusCommandFromOleCommandTarget(Guid cmdGroup, uint cmd, out bool handled) + { + handled = false; + // NOTE: We only want to support Cut/Copy/Paste/Delete/Rename commands + // if focus is in the project window. This means that we should only + // support these commands if they are dispatched via IVsUIHierarchy + // interface and not if they are dispatch through IOleCommandTarget + // during the command routing to the active project/hierarchy. + if(VsMenus.guidStandardCommandSet97 == cmdGroup) + { + + switch((VsCommands)cmd) + { + case VsCommands.Copy: + case VsCommands.Paste: + case VsCommands.Cut: + case VsCommands.Rename: + handled = true; + return QueryStatusResult.NOTSUPPORTED; + } + } + // The reference menu and the web reference menu should always be shown. + else if(cmdGroup == VsMenus.guidStandardCommandSet2K) + { + switch((VsCommands2K)cmd) + { + case VsCommands2K.ADDREFERENCE: + handled = true; + return QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + } + } + return QueryStatusResult.NOTSUPPORTED; + } + + /// + /// Specifies which command does not support multiple selection and should be disabled if multi-selected. + /// + /// Unique identifier of the command group + /// The command to be executed. + /// The list of selected nodes. + /// Specifies whether the menu was handled. + /// A QueryStatusResult describing the status of the menu. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Multi")] + protected virtual QueryStatusResult DisableCommandOnNodesThatDoNotSupportMultiSelection(Guid cmdGroup, uint cmd, IList selectedNodes, out bool handled) + { + handled = false; + QueryStatusResult queryResult = QueryStatusResult.NOTSUPPORTED; + if(selectedNodes == null || selectedNodes.Count == 1) + { + return queryResult; + } + + if(VsMenus.guidStandardCommandSet97 == cmdGroup) + { + switch((VsCommands)cmd) + { + case VsCommands.Cut: + case VsCommands.Copy: + // If the project node is selected then cut and copy is not supported. + if(selectedNodes.Contains(this.projectMgr)) + { + queryResult = QueryStatusResult.SUPPORTED | QueryStatusResult.INVISIBLE; + handled = true; + } + break; + + case VsCommands.Paste: + case VsCommands.NewFolder: + queryResult = QueryStatusResult.SUPPORTED | QueryStatusResult.INVISIBLE; + handled = true; + break; + } + } + else if(cmdGroup == VsMenus.guidStandardCommandSet2K) + { + switch((VsCommands2K)cmd) + { + case VsCommands2K.QUICKOBJECTSEARCH: + case VsCommands2K.SETASSTARTPAGE: + case VsCommands2K.ViewInClassDiagram: + queryResult = QueryStatusResult.SUPPORTED | QueryStatusResult.INVISIBLE; + handled = true; + break; + } + } + + return queryResult; + } + + /// + /// Handles command status on a node. Should be overridden by descendant nodes. If a command cannot be handled then the base should be called. + /// + /// A unique identifier of the command group. The pguidCmdGroup parameter can be NULL to specify the standard group. + /// The command to query status for. + /// Pointer to an OLECMDTEXT structure in which to return the name and/or status information of a single command. Can be NULL to indicate that the caller does not require this information. + /// An out parameter specifying the QueryStatusResult of the command. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "p")] + protected virtual int QueryStatusOnNode(Guid cmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result) + { + if(cmdGroup == VsMenus.guidStandardCommandSet2K) + { + if((VsCommands2K)cmd == VsCommands2K.SHOWALLFILES) + { + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + } + } + + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + /// + /// Disables commands when the project is in run/break mode. + /// / + /// Unique identifier of the command group + /// The command to be executed. + /// A QueryStatusResult describing the status of the menu. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"), SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "InCurrent")] + protected virtual bool DisableCmdInCurrentMode(Guid commandGroup, uint command) + { + if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return false; + } + + // Don't ask if it is not these two commandgroups. + if(commandGroup == VsMenus.guidStandardCommandSet97 || commandGroup == VsMenus.guidStandardCommandSet2K) + { + if(this.ProjectMgr.IsCurrentStateASuppressCommandsMode()) + { + if(commandGroup == VsMenus.guidStandardCommandSet97) + { + switch((VsCommands)command) + { + default: + break; + case VsCommands.AddExistingItem: + case VsCommands.AddNewItem: + case VsCommands.NewFolder: + case VsCommands.Remove: + case VsCommands.Cut: + case VsCommands.Paste: + case VsCommands.Copy: + case VsCommands.EditLabel: + case VsCommands.Rename: + case VsCommands.UnloadProject: + return true; + } + } + else if(commandGroup == VsMenus.guidStandardCommandSet2K) + { + switch((VsCommands2K)command) + { + default: + break; + case VsCommands2K.EXCLUDEFROMPROJECT: + case VsCommands2K.INCLUDEINPROJECT: + case VsCommands2K.ADDWEBREFERENCECTX: + case VsCommands2K.ADDWEBREFERENCE: + case VsCommands2K.ADDREFERENCE: + case VsCommands2K.SETASSTARTPAGE: + return true; + } + } + } + // If we are not in a cut or copy mode then disable the paste command + else if(!this.ProjectMgr.AllowPasteCommand()) + { + if(commandGroup == VsMenus.guidStandardCommandSet97 && (VsCommands)command == VsCommands.Paste) + { + return true; + } + } + } + + return false; + } + + + /// + /// Queries the object for the command status on a list of selected nodes. + /// + /// A unique identifier of the command group. + /// The number of commands in the prgCmds array + /// A caller-allocated array of OLECMD structures that indicate the commands for which the caller requires status information. This method fills the cmdf member of each structure with values taken from the OLECMDF enumeration + /// Pointer to an OLECMDTEXT structure in which to return the name and/or status information of a single command. Can be NULL to indicate that the caller does not require this information. + /// Specifies the origin of the command. Either it was called from the QueryStatusCommand on IVsUIHierarchy or from the IOleCommandTarget + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Cmds")] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "c")] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "p")] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "prg")] + protected virtual int QueryStatusSelection(Guid cmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText, CommandOrigin commandOrigin) + { + if(this.projectMgr.IsClosed) + { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + if(cmdGroup == Guid.Empty) + { + return (int)OleConstants.OLECMDERR_E_UNKNOWNGROUP; + } + + if (prgCmds == null) + { + throw new ArgumentNullException("prgCmds"); + } + + uint cmd = prgCmds[0].cmdID; + QueryStatusResult queryResult = QueryStatusResult.NOTSUPPORTED; + + // For now ask this node (that is the project node) to disable or enable a node. + // This is an optimization. Why should we ask each node for its current state? They all are in the same state. + // Also please note that we return QueryStatusResult.INVISIBLE instead of just QueryStatusResult.SUPPORTED. + // The reason is that if the project has nested projects, then providing just QueryStatusResult.SUPPORTED is not enough. + // What will happen is that the nested project will show grayed commands that belong to this project and does not belong to the nested project. (like special commands implemented by subclassed projects). + // The reason is that a special command comes in that is not handled because we are in debug mode. Then VsCore asks the nested project can you handle it. + // The nested project does not know about it, thus it shows it on the nested project as grayed. + if(this.DisableCmdInCurrentMode(cmdGroup, cmd)) + { + queryResult = QueryStatusResult.SUPPORTED | QueryStatusResult.INVISIBLE; + } + else + { + bool handled = false; + + if(commandOrigin == CommandOrigin.OleCommandTarget) + { + queryResult = this.QueryStatusCommandFromOleCommandTarget(cmdGroup, cmd, out handled); + } + + if(!handled) + { + IList selectedNodes = this.projectMgr.GetSelectedNodes(); + + // Want to disable in multiselect case. + if(selectedNodes != null && selectedNodes.Count > 1) + { + queryResult = this.DisableCommandOnNodesThatDoNotSupportMultiSelection(cmdGroup, cmd, selectedNodes, out handled); + } + + // Now go and do the job on the nodes. + if(!handled) + { + queryResult = this.QueryStatusSelectionOnNodes(selectedNodes, cmdGroup, cmd, pCmdText); + } + + } + } + + // Process the results set in the QueryStatusResult + if(queryResult != QueryStatusResult.NOTSUPPORTED) + { + // Set initial value + prgCmds[0].cmdf = (uint)OLECMDF.OLECMDF_SUPPORTED; + + if((queryResult & QueryStatusResult.ENABLED) != 0) + { + prgCmds[0].cmdf |= (uint)OLECMDF.OLECMDF_ENABLED; + } + + if((queryResult & QueryStatusResult.INVISIBLE) != 0) + { + prgCmds[0].cmdf |= (uint)OLECMDF.OLECMDF_INVISIBLE; + } + + if((queryResult & QueryStatusResult.LATCHED) != 0) + { + prgCmds[0].cmdf |= (uint)OLECMDF.OLECMDF_LATCHED; + } + + return VSConstants.S_OK; + } + + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + /// + /// Queries the selected nodes for the command status. + /// A command is supported iff any nodes supports it. + /// A command is enabled iff all nodes enable it. + /// A command is invisible iff any node sets invisibility. + /// A command is latched only if all are latched. + /// + /// The list of selected nodes. + /// A unique identifier of the command group. + /// The command id to query for. + /// Pointer to an OLECMDTEXT structure in which to return the name and/or status information of a single command. Can be NULL to indicate that the caller does not require this information. + /// Retuns the result of the query on the slected nodes. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "p")] + protected virtual QueryStatusResult QueryStatusSelectionOnNodes(IList selectedNodes, Guid cmdGroup, uint cmd, IntPtr pCmdText) + { + if(selectedNodes == null || selectedNodes.Count == 0) + { + return QueryStatusResult.NOTSUPPORTED; + } + + int result = 0; + bool supported = false; + bool enabled = true; + bool invisible = false; + bool latched = true; + QueryStatusResult tempQueryResult = QueryStatusResult.NOTSUPPORTED; + + foreach(HierarchyNode node in selectedNodes) + { + result = node.QueryStatusOnNode(cmdGroup, cmd, pCmdText, ref tempQueryResult); + if(result < 0) + { + break; + } + + // cmd is supported iff any node supports cmd + // cmd is enabled iff all nodes enable cmd + // cmd is invisible iff any node sets invisibility + // cmd is latched only if all are latched. + supported = supported || ((tempQueryResult & QueryStatusResult.SUPPORTED) != 0); + enabled = enabled && ((tempQueryResult & QueryStatusResult.ENABLED) != 0); + invisible = invisible || ((tempQueryResult & QueryStatusResult.INVISIBLE) != 0); + latched = latched && ((tempQueryResult & QueryStatusResult.LATCHED) != 0); + } + + QueryStatusResult queryResult = QueryStatusResult.NOTSUPPORTED; + + if(result >= 0 && supported) + { + queryResult = QueryStatusResult.SUPPORTED; + + if(enabled) + { + queryResult |= QueryStatusResult.ENABLED; + } + + if(invisible) + { + queryResult |= QueryStatusResult.INVISIBLE; + } + + if(latched) + { + queryResult |= QueryStatusResult.LATCHED; + } + } + + return queryResult; + } + + #endregion + protected virtual bool CanDeleteItem(__VSDELETEITEMOPERATION deleteOperation) + { + return this.ProjectMgr.CanProjectDeleteItems; + } + + /// + /// Overwrite this method to tell that you support the default icon for this node. + /// + /// + protected virtual bool CanShowDefaultIcon() + { + return false; + } + + /// + /// Performs save as operation for an item after the save as dialog has been processed. + /// + /// A pointer to the rdt + /// The newName of the item + /// + protected virtual int AfterSaveItemAs(IntPtr docData, string newName) + { + throw new NotImplementedException(); + } + + /// + /// The method that does the cleanup. + /// + /// Is the Dispose called by some internal member, or it is called by from GC. + protected virtual void Dispose(bool disposing) + { + if(this.isDisposed) + { + return; + } + + if(disposing) + { + // This will dispose any subclassed project node that implements IDisposable. + if(this.oleServiceProvider != null) + { + // Dispose the ole service provider object. + this.oleServiceProvider.Dispose(); + } + } + + this.isDisposed = true; + } + + /// + /// Sets the VSADDFILEFLAGS that will be used to call the IVsTrackProjectDocumentsEvents2 OnAddFiles + /// + /// The files to which an array of VSADDFILEFLAGS has to be specified. + /// + protected internal virtual VSADDFILEFLAGS[] GetAddFileFlags(string[] files) + { + if(files == null || files.Length == 0) + { + return new VSADDFILEFLAGS[1] { VSADDFILEFLAGS.VSADDFILEFLAGS_NoFlags }; + } + + VSADDFILEFLAGS[] addFileFlags = new VSADDFILEFLAGS[files.Length]; + + for(int i = 0; i < files.Length; i++) + { + addFileFlags[i] = VSADDFILEFLAGS.VSADDFILEFLAGS_NoFlags; + } + + return addFileFlags; + } + + /// + /// Sets the VSQUERYADDFILEFLAGS that will be used to call the IVsTrackProjectDocumentsEvents2 OnQueryAddFiles + /// + /// The files to which an array of VSADDFILEFLAGS has to be specified. + /// + protected internal virtual VSQUERYADDFILEFLAGS[] GetQueryAddFileFlags(string[] files) + { + if(files == null || files.Length == 0) + { + return new VSQUERYADDFILEFLAGS[1] { VSQUERYADDFILEFLAGS.VSQUERYADDFILEFLAGS_NoFlags }; + } + + VSQUERYADDFILEFLAGS[] queryAddFileFlags = new VSQUERYADDFILEFLAGS[files.Length]; + + for(int i = 0; i < files.Length; i++) + { + queryAddFileFlags[i] = VSQUERYADDFILEFLAGS.VSQUERYADDFILEFLAGS_NoFlags; + } + + return queryAddFileFlags; + } + + /// + /// Sets the VSREMOVEFILEFLAGS that will be used to call the IVsTrackProjectDocumentsEvents2 OnRemoveFiles + /// + /// The files to which an array of VSREMOVEFILEFLAGS has to be specified. + /// + protected internal virtual VSREMOVEFILEFLAGS[] GetRemoveFileFlags(string[] files) + { + if(files == null || files.Length == 0) + { + return new VSREMOVEFILEFLAGS[1] { VSREMOVEFILEFLAGS.VSREMOVEFILEFLAGS_NoFlags }; + } + + VSREMOVEFILEFLAGS[] removeFileFlags = new VSREMOVEFILEFLAGS[files.Length]; + + for(int i = 0; i < files.Length; i++) + { + removeFileFlags[i] = VSREMOVEFILEFLAGS.VSREMOVEFILEFLAGS_NoFlags; + } + + return removeFileFlags; + } + + /// + /// Sets the VSQUERYREMOVEFILEFLAGS that will be used to call the IVsTrackProjectDocumentsEvents2 OnQueryRemoveFiles + /// + /// The files to which an array of VSQUERYREMOVEFILEFLAGS has to be specified. + /// + protected internal virtual VSQUERYREMOVEFILEFLAGS[] GetQueryRemoveFileFlags(string[] files) + { + if(files == null || files.Length == 0) + { + return new VSQUERYREMOVEFILEFLAGS[1] { VSQUERYREMOVEFILEFLAGS.VSQUERYREMOVEFILEFLAGS_NoFlags }; + } + + VSQUERYREMOVEFILEFLAGS[] queryRemoveFileFlags = new VSQUERYREMOVEFILEFLAGS[files.Length]; + + for(int i = 0; i < files.Length; i++) + { + queryRemoveFileFlags[i] = VSQUERYREMOVEFILEFLAGS.VSQUERYREMOVEFILEFLAGS_NoFlags; + } + + return queryRemoveFileFlags; + } + + /// + /// This method should be overridden to provide the list of files and associated flags for source control. + /// + /// The list of files to be placed under source control. + /// The flags that are associated to the files. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")] + protected internal virtual void GetSccFiles(IList files, IList flags) + { + if(this.ExcludeNodeFromScc) + { + return; + } + + if(files == null) + { + throw new ArgumentNullException("files"); + } + + if(flags == null) + { + throw new ArgumentNullException("flags"); + } + + files.Add(this.GetMkDocument()); + + tagVsSccFilesFlags flagsToAdd = (this.firstChild != null && (this.firstChild is DependentFileNode)) ? tagVsSccFilesFlags.SFF_HasSpecialFiles : tagVsSccFilesFlags.SFF_NoFlags; + + flags.Add(flagsToAdd); + } + + /// + /// This method should be overridden to provide the list of special files and associated flags for source control. + /// + /// One of the file associated to the node. + /// The list of files to be placed under source control. + /// The flags that are associated to the files. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "scc")] + protected internal virtual void GetSccSpecialFiles(string sccFile, IList files, IList flags) + { + if(this.ExcludeNodeFromScc) + { + return; + } + + if(files == null) + { + throw new ArgumentNullException("files"); + } + + if(flags == null) + { + throw new ArgumentNullException("flags"); + } + } + + /// + /// Delete the item corresponding to the specified path from storage. + /// + /// Url of the item to delete + internal protected virtual void DeleteFromStorage(string path) + { + } + + /// + /// Determines whether a file change should be ignored or not. + /// + /// Flag indicating whether or not to ignore changes (true to ignore changes). + protected internal virtual void IgnoreItemFileChanges(bool ignoreFlag) + { + } + + /// + /// Called to determine whether a project item is reloadable. + /// + /// True if the project item is reloadable. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Reloadable")] + protected internal virtual bool IsItemReloadable() + { + return true; + } + + /// + /// Reloads an item. + /// + /// Reserved parameter defined at the IVsPersistHierarchyItem2::ReloadItem parameter. + protected internal virtual void ReloadItem(uint reserved) + { + + } + + /// + /// Handle the Copy operation to the clipboard + /// This method is typically overriden on the project node + /// + protected internal virtual int CopyToClipboard() + { + return VSConstants.E_NOTIMPL; + } + + /// + /// Handle the Cut operation to the clipboard + /// This method is typically overriden on the project node + /// + protected internal virtual int CutToClipboard() + { + return VSConstants.E_NOTIMPL; + } + + /// + /// Handle the paste from Clipboard command. + /// This method is typically overriden on the project node + /// + protected internal virtual int PasteFromClipboard(HierarchyNode targetNode) + { + return VSConstants.E_NOTIMPL; + } + + /// + /// Determines if the paste command should be allowed. + /// This method is typically overriden on the project node + /// + protected internal virtual bool AllowPasteCommand() + { + return false; ; + } + + /// + /// Register/Unregister for Clipboard events for the UiHierarchyWindow (solution explorer) + /// This method is typically overriden on the project node + /// + /// true for register, false for unregister + protected internal virtual void RegisterClipboardNotifications(bool value) + { + return; + } + #endregion + + #region public methods + + public void OnItemAdded(HierarchyNode parent, HierarchyNode child) + { + if (parent == null) + { + throw new ArgumentNullException("parent"); + } + + if (child == null) + { + throw new ArgumentNullException("child"); + } + + if(null != parent.onChildAdded) + { + HierarchyNodeEventArgs args = new HierarchyNodeEventArgs(child); + parent.onChildAdded(parent, args); + } + + HierarchyNode foo; + foo = this.projectMgr == null ? this : this.projectMgr; + + if(foo == this.projectMgr && (this.projectMgr.EventTriggeringFlag & ProjectNode.EventTriggering.DoNotTriggerHierarchyEvents) != 0) + { + return; + } + + HierarchyNode prev = child.PreviousSibling; + uint prevId = (prev != null) ? prev.hierarchyId : VSConstants.VSITEMID_NIL; + foreach(IVsHierarchyEvents sink in foo.hierarchyEventSinks) + { + int result = sink.OnItemAdded(parent.hierarchyId, prevId, child.hierarchyId); + if(ErrorHandler.Failed(result) && result != VSConstants.E_NOTIMPL) + { + ErrorHandler.ThrowOnFailure(result); + } + } + } + + + public void OnItemDeleted() + { + HierarchyNode foo; + foo = this.projectMgr == null ? this : this.projectMgr; + + if(foo == this.projectMgr && (this.projectMgr.EventTriggeringFlag & ProjectNode.EventTriggering.DoNotTriggerHierarchyEvents) != 0) + { + return; + } + + if(foo.hierarchyEventSinks.Count > 0) + { + // Note that in some cases (deletion of project node for example), an Advise + // may be removed while we are iterating over it. To get around this problem we + // take a snapshot of the advise list and walk that. + List clonedSink = new List(); + + foreach(IVsHierarchyEvents anEvent in foo.hierarchyEventSinks) + { + clonedSink.Add(anEvent); + } + + foreach(IVsHierarchyEvents clonedEvent in clonedSink) + { + int result = clonedEvent.OnItemDeleted(this.hierarchyId); + if(ErrorHandler.Failed(result) && result != VSConstants.E_NOTIMPL) + { + ErrorHandler.ThrowOnFailure(result); + } + } + } + } + + public void OnItemsAppended(HierarchyNode parent) + { + if(parent == null) + { + throw new ArgumentNullException("parent"); + } + + HierarchyNode foo; + foo = this.projectMgr == null ? this : this.projectMgr; + + if(foo == this.projectMgr && (this.projectMgr.EventTriggeringFlag & ProjectNode.EventTriggering.DoNotTriggerHierarchyEvents) != 0) + { + return; + } + + foreach(IVsHierarchyEvents sink in foo.hierarchyEventSinks) + { + int result = sink.OnItemsAppended(parent.hierarchyId); + + if(ErrorHandler.Failed(result) && result != VSConstants.E_NOTIMPL) + { + ErrorHandler.ThrowOnFailure(result); + } + } + } + + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "propid")] + public void OnPropertyChanged(HierarchyNode node, int propid, uint flags) + { + if(node == null) + { + throw new ArgumentNullException("node"); + } + HierarchyNode foo; + foo = this.projectMgr == null ? this : this.projectMgr; + if(foo == this.projectMgr && (this.projectMgr.EventTriggeringFlag & ProjectNode.EventTriggering.DoNotTriggerHierarchyEvents) != 0) + { + return; + } + + foreach(IVsHierarchyEvents sink in foo.hierarchyEventSinks) + { + int result = sink.OnPropertyChanged(node.hierarchyId, propid, flags); + + if(ErrorHandler.Failed(result) && result != VSConstants.E_NOTIMPL) + { + ErrorHandler.ThrowOnFailure(result); + } + } + } + + + public void OnInvalidateItems(HierarchyNode parent) + { + if(parent == null) + { + throw new ArgumentNullException("parent"); + } + HierarchyNode foo; + foo = this.projectMgr == null ? this : this.projectMgr; + if(foo == this.projectMgr && (this.projectMgr.EventTriggeringFlag & ProjectNode.EventTriggering.DoNotTriggerHierarchyEvents) != 0) + { + return; + } + + foreach(IVsHierarchyEvents sink in foo.hierarchyEventSinks) + { + int result = sink.OnInvalidateItems(parent.hierarchyId); + + if(ErrorHandler.Failed(result) && result != VSConstants.E_NOTIMPL) + { + ErrorHandler.ThrowOnFailure(result); + } + } + } + + /// + /// Causes the hierarchy to be redrawn. + /// + /// Used by the hierarchy to decide which element to redraw + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Re")] + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ReDraw")] + public virtual void ReDraw(UIHierarchyElement element) + { + + foreach(IVsHierarchyEvents sink in this.projectMgr.hierarchyEventSinks) + { + int result; + if((element & UIHierarchyElement.Icon) != 0) + { + result = sink.OnPropertyChanged(this.ID, (int)__VSHPROPID.VSHPROPID_IconIndex, 0); + Debug.Assert(ErrorHandler.Succeeded(result), "Redraw failed for node " + this.GetMkDocument()); + } + + if((element & UIHierarchyElement.Caption) != 0) + { + result = sink.OnPropertyChanged(this.ID, (int)__VSHPROPID.VSHPROPID_Caption, 0); + Debug.Assert(ErrorHandler.Succeeded(result), "Redraw failed for node " + this.GetMkDocument()); + } + + if((element & UIHierarchyElement.SccState) != 0) + { + result = sink.OnPropertyChanged(this.ID, (int)__VSHPROPID.VSHPROPID_StateIconIndex, 0); + Debug.Assert(ErrorHandler.Succeeded(result), "Redraw failed for node " + this.GetMkDocument()); + } + } + + } + + /// + /// Finds a non virtual hierarchy item by its project element. + /// + /// The Project element to find + /// The node found + public HierarchyNode FindChildByProjectElement(ProjectElement node) + { + if(node == null) + { + throw new ArgumentNullException("node"); + } + + for(HierarchyNode child = this.FirstChild; child != null; child = child.NextSibling) + { + if(!child.ItemNode.IsVirtual && child.ItemNode == node) + { + return child; + } + } + return null; + } + + public object GetService(Type type) + { + if(type == null) + { + throw new ArgumentNullException("type"); + } + + if(this.projectMgr.Site == null) return null; + return this.projectMgr.Site.GetService(type); + } + + + #endregion + + #region IDisposable + /// + /// The IDispose interface Dispose method for disposing the object determinastically. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + + #region IVsHierarchy methods + + public virtual int AdviseHierarchyEvents(IVsHierarchyEvents sink, out uint cookie) + { + cookie = this.hierarchyEventSinks.Add(sink) + 1; + return VSConstants.S_OK; + } + + + public virtual int Close() + { + DocumentManager manager = this.GetDocumentManager(); + try + { + if(manager != null) + { + manager.Close(__FRAMECLOSE.FRAMECLOSE_PromptSave); + } + + } + catch { } + finally + { + this.Dispose(true); + } + + return VSConstants.S_OK; + } + + + public virtual int GetCanonicalName(uint itemId, out string name) + { + HierarchyNode n = this.projectMgr.NodeFromItemId(itemId); + name = (n != null) ? n.GetCanonicalName() : null; + return VSConstants.S_OK; + } + + + public virtual int GetGuidProperty(uint itemId, int propid, out Guid guid) + { + guid = Guid.Empty; + HierarchyNode n = this.projectMgr.NodeFromItemId(itemId); + if(n != null) + { + int hr = n.GetGuidProperty(propid, out guid); + __VSHPROPID vspropId = (__VSHPROPID)propid; + CCITracing.TraceCall(vspropId.ToString() + "=" + guid.ToString()); + return hr; + } + if(guid == Guid.Empty) + { + return VSConstants.DISP_E_MEMBERNOTFOUND; + } + return VSConstants.S_OK; + } + + + public virtual int GetProperty(uint itemId, int propId, out object propVal) + { + propVal = null; + if(itemId != VSConstants.VSITEMID_ROOT && propId == (int)__VSHPROPID.VSHPROPID_IconImgList) + { + return VSConstants.DISP_E_MEMBERNOTFOUND; + } + + + HierarchyNode n = this.projectMgr.NodeFromItemId(itemId); + if(n != null) + { + propVal = n.GetProperty(propId); + } + if(propVal == null) + { + return VSConstants.DISP_E_MEMBERNOTFOUND; + } + return VSConstants.S_OK; + } + + + public virtual int GetNestedHierarchy(uint itemId, ref Guid iidHierarchyNested, out IntPtr ppHierarchyNested, out uint pItemId) + { + ppHierarchyNested = IntPtr.Zero; + pItemId = 0; + // If itemid is not a nested hierarchy we must return E_FAIL. + return VSConstants.E_FAIL; + } + + + public virtual int GetSite(out Microsoft.VisualStudio.OLE.Interop.IServiceProvider site) + { + site = this.projectMgr.Site.GetService(typeof(Microsoft.VisualStudio.OLE.Interop.IServiceProvider)) as Microsoft.VisualStudio.OLE.Interop.IServiceProvider; + return VSConstants.S_OK; + } + + + /// + /// the canonicalName of an item is it's URL, or better phrased, + /// the persistence data we put into @RelPath, which is a relative URL + /// to the root project + /// returning the itemID from this means scanning the list + /// + /// + /// + public virtual int ParseCanonicalName(string name, out uint itemId) + { + // we always start at the current node and go it's children down, so + // if you want to scan the whole tree, better call + // the root + itemId = 0; + + // The default implemenation will check for case insensitive comparision. + if(String.Compare(name, this.Url, StringComparison.OrdinalIgnoreCase) == 0) + { + itemId = this.hierarchyId; + return VSConstants.S_OK; + } + if(itemId == 0 && this.firstChild != null) + { + ErrorHandler.ThrowOnFailure(this.firstChild.ParseCanonicalName(name, out itemId)); + } + if(itemId == 0 && this.nextSibling != null) + { + ErrorHandler.ThrowOnFailure(this.nextSibling.ParseCanonicalName(name, out itemId)); + } + return VSConstants.S_OK; + } + + + public virtual int QueryClose(out int fCanClose) + { + fCanClose = 1; + return VSConstants.S_OK; + } + + + public virtual int SetGuidProperty(uint itemId, int propid, ref Guid guid) + { + HierarchyNode n = this.projectMgr.NodeFromItemId(itemId); + int rc = VSConstants.E_INVALIDARG; + if(n != null) + { + rc = n.SetGuidProperty(propid, ref guid); + } + return rc; + } + + + public virtual int SetProperty(uint itemId, int propid, object value) + { + HierarchyNode n = this.projectMgr.NodeFromItemId(itemId); + if(n != null) + { + return n.SetProperty(propid, value); + } + else + { + return VSConstants.DISP_E_MEMBERNOTFOUND; + } + } + + + public virtual int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider site) + { + return VSConstants.E_NOTIMPL; + } + + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2233:OperationsShouldNotOverflow", MessageId = "cookie-1")] + public virtual int UnadviseHierarchyEvents(uint cookie) + { + this.hierarchyEventSinks.RemoveAt(cookie - 1); + return VSConstants.S_OK; + } + + + public int Unused0() + { + return VSConstants.E_NOTIMPL; + } + + + public int Unused1() + { + return VSConstants.E_NOTIMPL; + } + + + public int Unused2() + { + return VSConstants.E_NOTIMPL; + } + + + public int Unused3() + { + return VSConstants.E_NOTIMPL; + } + + + public int Unused4() + { + return VSConstants.E_NOTIMPL; + } + #endregion + + #region IVsUIHierarchy methods + + public virtual int ExecCommand(uint itemId, ref Guid guidCmdGroup, uint nCmdId, uint nCmdExecOpt, IntPtr pvain, IntPtr p) + { + return this.InternalExecCommand(guidCmdGroup, nCmdId, nCmdExecOpt, pvain, p, CommandOrigin.UiHierarchy); + } + + public virtual int QueryStatusCommand(uint itemId, ref Guid guidCmdGroup, uint cCmds, OLECMD[] cmds, IntPtr pCmdText) + { + return this.QueryStatusSelection(guidCmdGroup, cCmds, cmds, pCmdText, CommandOrigin.UiHierarchy); + } + #endregion + + #region IVsPersistHierarchyItem2 methods + + /// + /// Determines whether the hierarchy item changed. + /// + /// Item identifier of the hierarchy item contained in VSITEMID. + /// Pointer to the IUnknown interface of the hierarchy item. + /// true if the hierarchy item changed. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int IsItemDirty(uint itemId, IntPtr docData, out int isDirty) + { + IVsPersistDocData pd = (IVsPersistDocData)Marshal.GetObjectForIUnknown(docData); + return ErrorHandler.ThrowOnFailure(pd.IsDocDataDirty(out isDirty)); + } + + /// + /// Saves the hierarchy item to disk. + /// + /// Flags whose values are taken from the VSSAVEFLAGS enumeration. + /// New filename when doing silent save as + /// Item identifier of the hierarchy item saved from VSITEMID. + /// Item identifier of the hierarchy item saved from VSITEMID. + /// [out] true if the save action was canceled. + /// [out] true if the save action was canceled. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + public virtual int SaveItem(VSSAVEFLAGS saveFlag, string silentSaveAsName, uint itemid, IntPtr docData, out int cancelled) + { + cancelled = 0; + + if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return VSConstants.E_FAIL; + } + + // Validate itemid + if(itemid == VSConstants.VSITEMID_ROOT || itemid == VSConstants.VSITEMID_SELECTION) + { + return VSConstants.E_INVALIDARG; + } + + HierarchyNode node = this.ProjectMgr.NodeFromItemId(itemid); + if(node == null) + { + return VSConstants.E_FAIL; + } + + string existingFileMoniker = node.GetMkDocument(); + + // We can only perform save if the document is open + if(docData == IntPtr.Zero) + { + string errorMessage = string.Format(CultureInfo.CurrentCulture, SR.GetString(SR.CanNotSaveFileNotOpeneInEditor, CultureInfo.CurrentUICulture), node.Url); + throw new InvalidOperationException(errorMessage); + } + + string docNew = String.Empty; + int returnCode = VSConstants.S_OK; + IPersistFileFormat ff = null; + IVsPersistDocData dd = null; + IVsUIShell shell = this.projectMgr.Site.GetService(typeof(SVsUIShell)) as IVsUIShell; + + if(shell == null) + { + return VSConstants.E_FAIL; + } + + try + { + //Save docdata object. + //For the saveas action a dialog is show in order to enter new location of file. + //In case of a save action and the file is readonly a dialog is also shown + //with a couple of options, SaveAs, Overwrite or Cancel. + ff = Marshal.GetObjectForIUnknown(docData) as IPersistFileFormat; + if(ff == null) + { + return VSConstants.E_FAIL; + } + if(VSSAVEFLAGS.VSSAVE_SilentSave == saveFlag) + { + ErrorHandler.ThrowOnFailure(shell.SaveDocDataToFile(saveFlag, ff, silentSaveAsName, out docNew, out cancelled)); + } + else + { + dd = Marshal.GetObjectForIUnknown(docData) as IVsPersistDocData; + if(dd == null) + { + return VSConstants.E_FAIL; + } + ErrorHandler.ThrowOnFailure(dd.SaveDocData(saveFlag, out docNew, out cancelled)); + } + + // We can be unloaded after the SaveDocData() call if the save caused a designer to add a file and this caused + // the project file to be reloaded (QEQS caused a newer version of the project file to be downloaded). So we check + // here. + if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + cancelled = 1; + return (int)OleConstants.OLECMDERR_E_CANCELED; + } + else + { + // if a SaveAs occurred we need to update to the fact our item's name has changed. + // this includes the following: + // 1. call RenameDocument on the RunningDocumentTable + // 2. update the full path name for the item in our hierarchy + // 3. a directory-based project may need to transfer the open editor to the + // MiscFiles project if the new file is saved outside of the project directory. + // This is accomplished by calling IVsExternalFilesManager::TransferDocument + + // we have three options for a saveas action to be performed + // 1. the flag was set (the save as command was triggered) + // 2. a silent save specifying a new document name + // 3. a save command was triggered but was not possible because the file has a read only attrib. Therefore + // the user has chosen to do a save as in the dialog that showed up + bool emptyOrSamePath = String.IsNullOrEmpty(docNew) || NativeMethods.IsSamePath(existingFileMoniker, docNew); + bool saveAs = ((saveFlag == VSSAVEFLAGS.VSSAVE_SaveAs)) || + ((saveFlag == VSSAVEFLAGS.VSSAVE_SilentSave) && !emptyOrSamePath) || + ((saveFlag == VSSAVEFLAGS.VSSAVE_Save) && !emptyOrSamePath); + + if(saveAs) + { + returnCode = node.AfterSaveItemAs(docData, docNew); + + // If it has been cancelled recover the old name. + if((returnCode == (int)OleConstants.OLECMDERR_E_CANCELED || returnCode == VSConstants.E_ABORT)) + { + // Cleanup. + this.DeleteFromStorage(docNew); + if(this is ProjectNode && File.Exists(docNew)) + { + File.Delete(docNew); + } + + if(ff != null) + { + returnCode = shell.SaveDocDataToFile(VSSAVEFLAGS.VSSAVE_SilentSave, ff, existingFileMoniker, out docNew, out cancelled); + } + } + else if(returnCode != VSConstants.S_OK) + { + ErrorHandler.ThrowOnFailure(returnCode); + } + } + } + } + catch(COMException e) + { + Trace.WriteLine("Exception :" + e.Message); + returnCode = e.ErrorCode; + + // Try to recover + // changed from MPFProj: + // http://mpfproj10.codeplex.com/WorkItem/View.aspx?WorkItemId=6982 + if(ff != null && cancelled == 0) + { + ErrorHandler.ThrowOnFailure(shell.SaveDocDataToFile(VSSAVEFLAGS.VSSAVE_SilentSave, ff, existingFileMoniker, out docNew, out cancelled)); + } + } + + return returnCode; + } + + /// + /// Flag indicating that changes to a file can be ignored when item is saved or reloaded. + /// + /// Specifies the item id from VSITEMID. + /// Flag indicating whether or not to ignore changes (1 to ignore, 0 to stop ignoring). + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int IgnoreItemFileChanges(uint itemId, int ignoreFlag) + { + #region precondition + if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return VSConstants.E_FAIL; + } + #endregion + + HierarchyNode n = this.ProjectMgr.NodeFromItemId(itemId); + if(n != null) + { + n.IgnoreItemFileChanges(ignoreFlag == 0 ? false : true); + } + + return VSConstants.S_OK; + } + + /// + /// Called to determine whether a project item is reloadable before calling ReloadItem. + /// + /// Item identifier of an item in the hierarchy. Valid values are VSITEMID_NIL, VSITEMID_ROOT and VSITEMID_SELECTION. + /// A flag indicating that the project item is reloadable (1 for reloadable, 0 for non-reloadable). + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Reloadable")] + public virtual int IsItemReloadable(uint itemId, out int isReloadable) + { + isReloadable = 0; + + if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return VSConstants.E_FAIL; + } + + HierarchyNode n = this.ProjectMgr.NodeFromItemId(itemId); + if(n != null) + { + isReloadable = (n.IsItemReloadable()) ? 1 : 0; + } + + return VSConstants.S_OK; + } + + /// + /// Called to reload a project item. + /// + /// Specifies itemid from VSITEMID. + /// Reserved. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int ReloadItem(uint itemId, uint reserved) + { + #region precondition + if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return VSConstants.E_FAIL; + } + #endregion + + HierarchyNode n = this.ProjectMgr.NodeFromItemId(itemId); + if(n != null) + { + n.ReloadItem(reserved); + } + + return VSConstants.S_OK; + } + #endregion + + #region IOleCommandTarget methods + /// + /// CommandTarget.Exec is called for most major operations if they are NOT UI based. Otherwise IVSUInode::exec is called first + /// + public virtual int Exec(ref Guid guidCmdGroup, uint nCmdId, uint nCmdExecOpt, IntPtr pvaIn, IntPtr pvaOut) + { + return this.InternalExecCommand(guidCmdGroup, nCmdId, nCmdExecOpt, pvaIn, pvaOut, CommandOrigin.OleCommandTarget); + } + + /// + /// Queries the object for the command status + /// + /// we only support one command at a time, i.e. the first member in the OLECMD array + public virtual int QueryStatus(ref Guid guidCmdGroup, uint cCmds, OLECMD[] prgCmds, IntPtr pCmdText) + { + return this.QueryStatusSelection(guidCmdGroup, cCmds, prgCmds, pCmdText, CommandOrigin.OleCommandTarget); + } + #endregion + + #region IVsHierarchyDeleteHandler methods + + public virtual int DeleteItem(uint delItemOp, uint itemId) + { + if(itemId == VSConstants.VSITEMID_SELECTION) + { + return VSConstants.E_INVALIDARG; + } + + HierarchyNode node = this.projectMgr.NodeFromItemId(itemId); + if(node != null) + { + node.Remove((delItemOp & (uint)__VSDELETEITEMOPERATION.DELITEMOP_DeleteFromStorage) != 0); + return VSConstants.S_OK; + } + + return VSConstants.E_FAIL; + } + + + public virtual int QueryDeleteItem(uint delItemOp, uint itemId, out int candelete) + { + candelete = 0; + if(itemId == VSConstants.VSITEMID_SELECTION) + { + return VSConstants.E_INVALIDARG; + } + + if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return VSConstants.E_FAIL; + } + + // We ask the project what state it is. If he is a state that should not allow delete then we return. + if(this.ProjectMgr.IsCurrentStateASuppressCommandsMode()) + { + return VSConstants.S_OK; + } + + HierarchyNode node = this.projectMgr.NodeFromItemId(itemId); + + if(node == null) + { + return VSConstants.E_FAIL; + } + + // Ask the nodes if they can remove the item. + bool canDeleteItem = node.CanDeleteItem((__VSDELETEITEMOPERATION)delItemOp); + if(canDeleteItem) + { + candelete = 1; + } + + return VSConstants.S_OK; + } + #endregion + + #region IVsHierarchyDropDataSource2 methods + + public virtual int GetDropInfo(out uint pdwOKEffects, out Microsoft.VisualStudio.OLE.Interop.IDataObject ppDataObject, out IDropSource ppDropSource) + { + pdwOKEffects = (uint)DropEffect.None; + ppDataObject = null; + ppDropSource = null; + return VSConstants.E_NOTIMPL; + } + + public virtual int OnDropNotify(int fDropped, uint dwEffects) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnBeforeDropNotify(Microsoft.VisualStudio.OLE.Interop.IDataObject pDataObject, uint dwEffect, out int fCancelDrop) + { + pDataObject = null; + fCancelDrop = 0; + return VSConstants.E_NOTIMPL; + } + #endregion + + #region IVsHierarchyDropDataTarget methods + + public virtual int DragEnter(Microsoft.VisualStudio.OLE.Interop.IDataObject pDataObject, uint grfKeyState, uint itemid, ref uint pdwEffect) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int DragLeave() + { + return VSConstants.E_NOTIMPL; + } + + public virtual int DragOver(uint grfKeyState, uint itemid, ref uint pdwEffect) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int Drop(Microsoft.VisualStudio.OLE.Interop.IDataObject pDataObject, uint grfKeyState, uint itemid, ref uint pdwEffect) + { + return VSConstants.E_NOTIMPL; + } + #endregion + + #region helper methods + internal HierarchyNode FindChild(string name) + { + if(String.IsNullOrEmpty(name)) + { + return null; + } + + HierarchyNode result; + for(HierarchyNode child = this.firstChild; child != null; child = child.NextSibling) + { + if(!String.IsNullOrEmpty(child.VirtualNodeName) && String.Compare(child.VirtualNodeName, name, StringComparison.OrdinalIgnoreCase) == 0) + { + return child; + } + // If it is a foldernode then it has a virtual name but we want to find folder nodes by the document moniker or url + else if((String.IsNullOrEmpty(child.VirtualNodeName) || (child is FolderNode)) && + (NativeMethods.IsSamePath(child.GetMkDocument(), name) || NativeMethods.IsSamePath(child.Url, name))) + { + return child; + } + + result = child.FindChild(name); + if(result != null) + { + return result; + } + } + return null; + } + + /// + /// Recursively find all nodes of type T + /// + /// The type of hierachy node being serched for + /// A list of nodes of type T + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal void FindNodesOfType(List nodes) + where T : HierarchyNode + { + for(HierarchyNode n = this.FirstChild; n != null; n = n.NextSibling) + { + if(n is T) + { + T nodeAsT = (T)n; + nodes.Add(nodeAsT); + } + + n.FindNodesOfType(nodes); + } + } + + /// + /// Adds an item from a project refererence to target node. + /// + /// + /// + internal bool AddFileToNodeFromProjectReference(string projectRef, HierarchyNode targetNode) + { + if(String.IsNullOrEmpty(projectRef)) + { + throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "projectRef"); + } + + if(targetNode == null) + { + throw new ArgumentNullException("targetNode"); + } + + IVsSolution solution = this.GetService(typeof(IVsSolution)) as IVsSolution; + if(solution == null) + { + throw new InvalidOperationException(); + } + + uint itemidLoc; + IVsHierarchy hierarchy; + string str; + VSUPDATEPROJREFREASON[] reason = new VSUPDATEPROJREFREASON[1]; + ErrorHandler.ThrowOnFailure(solution.GetItemOfProjref(projectRef, out hierarchy, out itemidLoc, out str, reason)); + if(hierarchy == null) + { + throw new InvalidOperationException(); + } + + // This will throw invalid cast exception if the hierrachy is not a project. + IVsProject project = (IVsProject)hierarchy; + + string moniker; + ErrorHandler.ThrowOnFailure(project.GetMkDocument(itemidLoc, out moniker)); + string[] files = new String[1] { moniker }; + VSADDRESULT[] vsaddresult = new VSADDRESULT[1]; + vsaddresult[0] = VSADDRESULT.ADDRESULT_Failure; + int addResult = targetNode.ProjectMgr.AddItem(targetNode.ID, VSADDITEMOPERATION.VSADDITEMOP_OPENFILE, null, 0, files, IntPtr.Zero, vsaddresult); + if(addResult != VSConstants.S_OK && addResult != VSConstants.S_FALSE && addResult != (int)OleConstants.OLECMDERR_E_CANCELED) + { + ErrorHandler.ThrowOnFailure(addResult); + return false; + } + return (vsaddresult[0] == VSADDRESULT.ADDRESULT_Success); + } + + internal void InstantiateItemsDraggedOrCutOrCopiedList() + { + this.itemsDraggedOrCutOrCopied = new List(); + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/IDEBuildLogger.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/IDEBuildLogger.cs new file mode 100644 index 0000000000..33656a3e17 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/IDEBuildLogger.cs @@ -0,0 +1,621 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Windows.Forms.Design; +using System.Collections.Generic; +using System.Collections.Concurrent; +using System.Diagnostics; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Text; +using System.Diagnostics.CodeAnalysis; +using System.Threading; +using System.Windows.Threading; +using Microsoft.Build.Framework; +using Microsoft.Build.Utilities; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TextManager.Interop; +using Microsoft.Win32; +using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// This class implements an MSBuild logger that output events to VS outputwindow and tasklist. + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "IDE")] + internal class IDEBuildLogger : Logger + { + #region fields + + // TODO: Remove these constants when we have a version that suppoerts getting the verbosity using automation. + private string buildVerbosityRegistryRoot = @"Software\Microsoft\VisualStudio\10.0"; + private const string buildVerbosityRegistrySubKey = @"General"; + private const string buildVerbosityRegistryKey = "MSBuildLoggerVerbosity"; + + private int currentIndent; + private IVsOutputWindowPane outputWindowPane; + private string errorString = SR.GetString(SR.Error, CultureInfo.CurrentUICulture); + private string warningString = SR.GetString(SR.Warning, CultureInfo.CurrentUICulture); + private TaskProvider taskProvider; + private IVsHierarchy hierarchy; + private IServiceProvider serviceProvider; + private Dispatcher dispatcher; + private bool haveCachedVerbosity = false; + + // Queues to manage Tasks and Error output plus message logging + private ConcurrentQueue> taskQueue; + private ConcurrentQueue outputQueue; + + #endregion + + #region properties + + public IServiceProvider ServiceProvider + { + get { return this.serviceProvider; } + } + + public string WarningString + { + get { return this.warningString; } + set { this.warningString = value; } + } + + public string ErrorString + { + get { return this.errorString; } + set { this.errorString = value; } + } + + /// + /// When the build is not a "design time" (background or secondary) build this is True + /// + /// + /// The only known way to detect an interactive build is to check this.outputWindowPane for null. + /// + protected bool InteractiveBuild + { + get { return this.outputWindowPane != null; } + } + + /// + /// When building from within VS, setting this will + /// enable the logger to retrive the verbosity from + /// the correct registry hive. + /// + internal string BuildVerbosityRegistryRoot + { + get { return this.buildVerbosityRegistryRoot; } + set + { + this.buildVerbosityRegistryRoot = value; + } + } + + /// + /// Set to null to avoid writing to the output window + /// + internal IVsOutputWindowPane OutputWindowPane + { + get { return this.outputWindowPane; } + set { this.outputWindowPane = value; } + } + + #endregion + + #region ctors + + /// + /// Constructor. Inititialize member data. + /// + public IDEBuildLogger(IVsOutputWindowPane output, TaskProvider taskProvider, IVsHierarchy hierarchy) + { + if (taskProvider == null) + throw new ArgumentNullException("taskProvider"); + if (hierarchy == null) + throw new ArgumentNullException("hierarchy"); + + Trace.WriteLineIf(Thread.CurrentThread.GetApartmentState() != ApartmentState.STA, "WARNING: IDEBuildLogger constructor running on the wrong thread."); + + IOleServiceProvider site; + Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(hierarchy.GetSite(out site)); + + this.taskProvider = taskProvider; + this.outputWindowPane = output; + this.hierarchy = hierarchy; + this.serviceProvider = new ServiceProvider(site); + this.dispatcher = Dispatcher.CurrentDispatcher; + } + + #endregion + + #region overridden methods + + /// + /// Overridden from the Logger class. + /// + public override void Initialize(IEventSource eventSource) + { + if (null == eventSource) + { + throw new ArgumentNullException("eventSource"); + } + + this.taskQueue = new ConcurrentQueue>(); + this.outputQueue = new ConcurrentQueue(); + + eventSource.BuildStarted += new BuildStartedEventHandler(BuildStartedHandler); + eventSource.BuildFinished += new BuildFinishedEventHandler(BuildFinishedHandler); + eventSource.ProjectStarted += new ProjectStartedEventHandler(ProjectStartedHandler); + eventSource.ProjectFinished += new ProjectFinishedEventHandler(ProjectFinishedHandler); + eventSource.TargetStarted += new TargetStartedEventHandler(TargetStartedHandler); + eventSource.TargetFinished += new TargetFinishedEventHandler(TargetFinishedHandler); + eventSource.TaskStarted += new TaskStartedEventHandler(TaskStartedHandler); + eventSource.TaskFinished += new TaskFinishedEventHandler(TaskFinishedHandler); + eventSource.CustomEventRaised += new CustomBuildEventHandler(CustomHandler); + eventSource.ErrorRaised += new BuildErrorEventHandler(ErrorHandler); + eventSource.WarningRaised += new BuildWarningEventHandler(WarningHandler); + eventSource.MessageRaised += new BuildMessageEventHandler(MessageHandler); + } + + #endregion + + #region event delegates + + /// + /// This is the delegate for BuildStartedHandler events. + /// + protected virtual void BuildStartedHandler(object sender, BuildStartedEventArgs buildEvent) + { + // NOTE: This may run on a background thread! + ClearCachedVerbosity(); + ClearQueuedOutput(); + ClearQueuedTasks(); + + QueueOutputEvent(MessageImportance.Low, buildEvent); + } + + /// + /// This is the delegate for BuildFinishedHandler events. + /// + /// + /// + protected virtual void BuildFinishedHandler(object sender, BuildFinishedEventArgs buildEvent) + { + // NOTE: This may run on a background thread! + MessageImportance importance = buildEvent.Succeeded ? MessageImportance.Low : MessageImportance.High; + QueueOutputText(importance, Environment.NewLine); + QueueOutputEvent(importance, buildEvent); + + // flush output and error queues + ReportQueuedOutput(); + ReportQueuedTasks(); + } + + /// + /// This is the delegate for ProjectStartedHandler events. + /// + protected virtual void ProjectStartedHandler(object sender, ProjectStartedEventArgs buildEvent) + { + // NOTE: This may run on a background thread! + QueueOutputEvent(MessageImportance.Low, buildEvent); + } + + /// + /// This is the delegate for ProjectFinishedHandler events. + /// + protected virtual void ProjectFinishedHandler(object sender, ProjectFinishedEventArgs buildEvent) + { + // NOTE: This may run on a background thread! + QueueOutputEvent(buildEvent.Succeeded ? MessageImportance.Low : MessageImportance.High, buildEvent); + } + + /// + /// This is the delegate for TargetStartedHandler events. + /// + protected virtual void TargetStartedHandler(object sender, TargetStartedEventArgs buildEvent) + { + // NOTE: This may run on a background thread! + QueueOutputEvent(MessageImportance.Low, buildEvent); + IndentOutput(); + } + + /// + /// This is the delegate for TargetFinishedHandler events. + /// + protected virtual void TargetFinishedHandler(object sender, TargetFinishedEventArgs buildEvent) + { + // NOTE: This may run on a background thread! + UnindentOutput(); + QueueOutputEvent(MessageImportance.Low, buildEvent); + } + + /// + /// This is the delegate for TaskStartedHandler events. + /// + protected virtual void TaskStartedHandler(object sender, TaskStartedEventArgs buildEvent) + { + // NOTE: This may run on a background thread! + QueueOutputEvent(MessageImportance.Low, buildEvent); + IndentOutput(); + } + + /// + /// This is the delegate for TaskFinishedHandler events. + /// + protected virtual void TaskFinishedHandler(object sender, TaskFinishedEventArgs buildEvent) + { + // NOTE: This may run on a background thread! + UnindentOutput(); + QueueOutputEvent(MessageImportance.Low, buildEvent); + } + + /// + /// This is the delegate for CustomHandler events. + /// + /// + /// + protected virtual void CustomHandler(object sender, CustomBuildEventArgs buildEvent) + { + // NOTE: This may run on a background thread! + QueueOutputEvent(MessageImportance.High, buildEvent); + } + + /// + /// This is the delegate for error events. + /// + protected virtual void ErrorHandler(object sender, BuildErrorEventArgs errorEvent) + { + // NOTE: This may run on a background thread! + QueueOutputText(GetFormattedErrorMessage(errorEvent.File, errorEvent.LineNumber, errorEvent.ColumnNumber, false, errorEvent.Code, errorEvent.Message)); + QueueTaskEvent(errorEvent); + } + + /// + /// This is the delegate for warning events. + /// + protected virtual void WarningHandler(object sender, BuildWarningEventArgs warningEvent) + { + // NOTE: This may run on a background thread! + QueueOutputText(MessageImportance.High, GetFormattedErrorMessage(warningEvent.File, warningEvent.LineNumber, warningEvent.ColumnNumber, true, warningEvent.Code, warningEvent.Message)); + QueueTaskEvent(warningEvent); + } + + /// + /// This is the delegate for Message event types + /// + protected virtual void MessageHandler(object sender, BuildMessageEventArgs messageEvent) + { + // NOTE: This may run on a background thread! + QueueOutputEvent(messageEvent.Importance, messageEvent); + } + + #endregion + + #region output queue + + protected void QueueOutputEvent(MessageImportance importance, BuildEventArgs buildEvent) + { + // NOTE: This may run on a background thread! + if (LogAtImportance(importance) && !string.IsNullOrEmpty(buildEvent.Message)) + { + StringBuilder message = new StringBuilder(this.currentIndent + buildEvent.Message.Length); + if (this.currentIndent > 0) + { + message.Append('\t', this.currentIndent); + } + message.AppendLine(buildEvent.Message); + + QueueOutputText(message.ToString()); + } + } + + protected void QueueOutputText(MessageImportance importance, string text) + { + // NOTE: This may run on a background thread! + if (LogAtImportance(importance)) + { + QueueOutputText(text); + } + } + + protected void QueueOutputText(string text) + { + // NOTE: This may run on a background thread! + if (this.OutputWindowPane != null) + { + // Enqueue the output text + this.outputQueue.Enqueue(text); + + // We want to interactively report the output. But we dont want to dispatch + // more than one at a time, otherwise we might overflow the main thread's + // message queue. So, we only report the output if the queue was empty. + if (this.outputQueue.Count == 1) + { + ReportQueuedOutput(); + } + } + } + + private void IndentOutput() + { + // NOTE: This may run on a background thread! + this.currentIndent++; + } + + private void UnindentOutput() + { + // NOTE: This may run on a background thread! + this.currentIndent--; + } + + private void ReportQueuedOutput() + { + // NOTE: This may run on a background thread! + // We need to output this on the main thread. We must use BeginInvoke because the main thread may not be pumping events yet. + BeginInvokeWithErrorMessage(this.serviceProvider, this.dispatcher, () => + { + if (this.OutputWindowPane != null) + { + string outputString; + + while (this.outputQueue.TryDequeue(out outputString)) + { + Microsoft.VisualStudio.ErrorHandler.ThrowOnFailure(this.OutputWindowPane.OutputString(outputString)); + } + } + }); + } + + private void ClearQueuedOutput() + { + // NOTE: This may run on a background thread! + this.outputQueue = new ConcurrentQueue(); + } + + #endregion output queue + + #region task queue + + protected void QueueTaskEvent(BuildEventArgs errorEvent) + { + this.taskQueue.Enqueue(() => + { + ErrorTask task = new ErrorTask(); + + if (errorEvent is BuildErrorEventArgs) + { + BuildErrorEventArgs errorArgs = (BuildErrorEventArgs)errorEvent; + task.Document = errorArgs.File; + task.ErrorCategory = TaskErrorCategory.Error; + task.Line = errorArgs.LineNumber - 1; // The task list does +1 before showing this number. + task.Column = errorArgs.ColumnNumber; + task.Priority = TaskPriority.High; + } + else if (errorEvent is BuildWarningEventArgs) + { + BuildWarningEventArgs warningArgs = (BuildWarningEventArgs)errorEvent; + task.Document = warningArgs.File; + task.ErrorCategory = TaskErrorCategory.Warning; + task.Line = warningArgs.LineNumber - 1; // The task list does +1 before showing this number. + task.Column = warningArgs.ColumnNumber; + task.Priority = TaskPriority.Normal; + } + + task.Text = errorEvent.Message; + task.Category = TaskCategory.BuildCompile; + task.HierarchyItem = hierarchy; + + return task; + }); + + // NOTE: Unlike output we dont want to interactively report the tasks. So we never queue + // call ReportQueuedTasks here. We do this when the build finishes. + } + + private void ReportQueuedTasks() + { + // NOTE: This may run on a background thread! + // We need to output this on the main thread. We must use BeginInvoke because the main thread may not be pumping events yet. + BeginInvokeWithErrorMessage(this.serviceProvider, this.dispatcher, () => + { + this.taskProvider.SuspendRefresh(); + try + { + Func taskFunc; + + while (this.taskQueue.TryDequeue(out taskFunc)) + { + // Create the error task + ErrorTask task = taskFunc(); + + // Log the task + this.taskProvider.Tasks.Add(task); + } + } + finally + { + this.taskProvider.ResumeRefresh(); + } + }); + } + + private void ClearQueuedTasks() + { + // NOTE: This may run on a background thread! + this.taskQueue = new ConcurrentQueue>(); + + if (this.InteractiveBuild) + { + // We need to clear this on the main thread. We must use BeginInvoke because the main thread may not be pumping events yet. + BeginInvokeWithErrorMessage(this.serviceProvider, this.dispatcher, () => + { + this.taskProvider.Tasks.Clear(); + }); + } + } + + #endregion task queue + + #region helpers + + /// + /// This method takes a MessageImportance and returns true if messages + /// at importance i should be loggeed. Otherwise return false. + /// + private bool LogAtImportance(MessageImportance importance) + { + // If importance is too low for current settings, ignore the event + bool logIt = false; + + this.SetVerbosity(); + + switch (this.Verbosity) + { + case LoggerVerbosity.Quiet: + logIt = false; + break; + case LoggerVerbosity.Minimal: + logIt = (importance == MessageImportance.High); + break; + case LoggerVerbosity.Normal: + // Falling through... + case LoggerVerbosity.Detailed: + logIt = (importance != MessageImportance.Low); + break; + case LoggerVerbosity.Diagnostic: + logIt = true; + break; + default: + Debug.Fail("Unknown Verbosity level. Ignoring will cause everything to be logged"); + break; + } + + return logIt; + } + + /// + /// Format error messages for the task list + /// + private string GetFormattedErrorMessage( + string fileName, + int line, + int column, + bool isWarning, + string errorNumber, + string errorText) + { + string errorCode = isWarning ? this.WarningString : this.ErrorString; + + StringBuilder message = new StringBuilder(); + if (!string.IsNullOrEmpty(fileName)) + { + message.AppendFormat(CultureInfo.CurrentCulture, "{0}({1},{2}):", fileName, line, column); + } + message.AppendFormat(CultureInfo.CurrentCulture, " {0} {1}: {2}", errorCode, errorNumber, errorText); + message.AppendLine(); + + return message.ToString(); + } + + /// + /// Sets the verbosity level. + /// + private void SetVerbosity() + { + // TODO: This should be replaced when we have a version that supports automation. + if (!this.haveCachedVerbosity) + { + string verbosityKey = String.Format(CultureInfo.InvariantCulture, @"{0}\{1}", BuildVerbosityRegistryRoot, buildVerbosityRegistrySubKey); + using (RegistryKey subKey = Registry.CurrentUser.OpenSubKey(verbosityKey)) + { + if (subKey != null) + { + object valueAsObject = subKey.GetValue(buildVerbosityRegistryKey); + if (valueAsObject != null) + { + this.Verbosity = (LoggerVerbosity)((int)valueAsObject); + } + } + } + + this.haveCachedVerbosity = true; + } + } + + /// + /// Clear the cached verbosity, so that it will be re-evaluated from the build verbosity registry key. + /// + private void ClearCachedVerbosity() + { + this.haveCachedVerbosity = false; + } + + #endregion helpers + + #region exception handling helpers + + /// + /// Call Dispatcher.BeginInvoke, showing an error message if there was a non-critical exception. + /// + /// service provider + /// dispatcher + /// action to invoke + private static void BeginInvokeWithErrorMessage(IServiceProvider serviceProvider, Dispatcher dispatcher, Action action) + { + dispatcher.BeginInvoke(new Action(() => CallWithErrorMessage(serviceProvider, action))); + } + + /// + /// Show error message if exception is caught when invoking a method + /// + /// service provider + /// action to invoke + private static void CallWithErrorMessage(IServiceProvider serviceProvider, Action action) + { + try + { + action(); + } + catch (Exception ex) + { + if (Microsoft.VisualStudio.ErrorHandler.IsCriticalException(ex)) + { + throw; + } + + ShowErrorMessage(serviceProvider, ex); + } + } + + /// + /// Show error window about the exception + /// + /// service provider + /// exception + private static void ShowErrorMessage(IServiceProvider serviceProvider, Exception exception) + { + IUIService UIservice = (IUIService)serviceProvider.GetService(typeof(IUIService)); + if (UIservice != null && exception != null) + { + UIservice.ShowError(exception); + } + } + + #endregion exception handling helpers + } +} \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ImageHandler.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ImageHandler.cs new file mode 100644 index 0000000000..3682544c43 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ImageHandler.cs @@ -0,0 +1,207 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Drawing; +using System.IO; +using System.Windows.Forms; +using Microsoft.VisualStudio; + +namespace Microsoft.VisualStudio.Project +{ + public class ImageHandler : IDisposable + { + private ImageList imageList; + private List iconHandles; + private static volatile object Mutex; + private bool isDisposed; + + /// + /// Initializes the class. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1810:InitializeReferenceTypeStaticFieldsInline")] + static ImageHandler() + { + Mutex = new object(); + } + + /// + /// Builds an empty ImageHandler object. + /// + public ImageHandler() + { + } + + /// + /// Builds an ImageHandler object from a Stream providing the bitmap that + /// stores the images for the image list. + /// + public ImageHandler(Stream resourceStream) + { + if(null == resourceStream) + { + throw new ArgumentNullException("resourceStream"); + } + imageList = Utilities.GetImageList(resourceStream); + } + + /// + /// Builds an ImageHandler object from an ImageList object. + /// + public ImageHandler(ImageList list) + { + if(null == list) + { + throw new ArgumentNullException("list"); + } + imageList = list; + } + + /// + /// Closes the ImageHandler object freeing its resources. + /// + public void Close() + { + if(null != iconHandles) + { + foreach(IntPtr hnd in iconHandles) + { + if(hnd != IntPtr.Zero) + { + NativeMethods.DestroyIcon(hnd); + } + } + iconHandles = null; + } + + if(null != imageList) + { + imageList.Dispose(); + imageList = null; + } + } + + /// + /// Add an image to the ImageHandler. + /// + /// the image object to be added. + public void AddImage(Image image) + { + if(null == image) + { + throw new ArgumentNullException("image"); + } + if(null == imageList) + { + imageList = new ImageList(); + } + imageList.Images.Add(image); + if(null != iconHandles) + { + iconHandles.Add(IntPtr.Zero); + } + } + + /// + /// Get or set the ImageList object for this ImageHandler. + /// + public ImageList ImageList + { + get { return imageList; } + set + { + Close(); + imageList = value; + } + } + + /// + /// Returns the handle to an icon build from the image of index + /// iconIndex in the image list. + /// + public IntPtr GetIconHandle(int iconIndex) + { + // Verify that the object is in a consistent state. + if((null == imageList)) + { + throw new InvalidOperationException(); + } + // Make sure that the list of handles is initialized. + if(null == iconHandles) + { + InitHandlesList(); + } + + // Verify that the index is inside the expected range. + if((iconIndex < 0) || (iconIndex >= iconHandles.Count)) + { + throw new ArgumentOutOfRangeException("iconIndex"); + } + + // Check if the icon is in the cache. + if(IntPtr.Zero == iconHandles[iconIndex]) + { + Bitmap bitmap = imageList.Images[iconIndex] as Bitmap; + // If the image is not a bitmap, then we can not build the icon, + // so we have to return a null handle. + if(null == bitmap) + { + return IntPtr.Zero; + } + + iconHandles[iconIndex] = bitmap.GetHicon(); + } + + return iconHandles[iconIndex]; + } + + private void InitHandlesList() + { + iconHandles = new List(imageList.Images.Count); + for(int i = 0; i < imageList.Images.Count; ++i) + { + iconHandles.Add(IntPtr.Zero); + } + } + + #region IDisposable Members + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + private void Dispose(bool disposing) + { + if(!this.isDisposed) + { + lock(Mutex) + { + if(disposing) + { + this.imageList.Dispose(); + } + + this.isDisposed = true; + } + } + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Interfaces.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Interfaces.cs new file mode 100644 index 0000000000..59c7a6736f --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Interfaces.cs @@ -0,0 +1,168 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.Shell.Interop; +using MSBuild = Microsoft.Build.Evaluation; + +namespace Microsoft.VisualStudio.Project +{ + + /// + /// This interface defines the rules for handling build dependency on a project container. + /// + /// Normally this should be an internal interface but since it shouldbe available for the aggregator it must be made public. + [ComVisible(true)] + [CLSCompliant(false)] + public interface IBuildDependencyOnProjectContainer + { + /// + /// Defines whether the nested projects should be build with the parent project. + /// + bool BuildNestedProjectsOnBuild + { + get; + set; + } + + /// + /// Enumerates the nested hierachies present that will participate in the build dependency update. + /// + /// A list of hierrachies. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Hierachies")] + IVsHierarchy[] EnumNestedHierachiesForBuildDependency(); + } + + /// + /// Interface for manipulating build dependency + /// + /// Normally this should be an internal interface but since it shouldbe available for the aggregator it must be made public. + [ComVisible(true)] + [CLSCompliant(false)] + public interface IBuildDependencyUpdate + { + /// + /// Defines a container for storing BuildDependencies + /// + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + IVsBuildDependency[] BuildDependencies + { + get; + } + + /// + /// Adds a BuildDependency to the container + /// + /// The dependency to add + void AddBuildDependency(IVsBuildDependency dependency); + + /// + /// Removes the builddependency from teh container. + /// + /// The dependency to add + void RemoveBuildDependency(IVsBuildDependency dependency); + + } + + /// + /// Provides access to the reference data container. + /// + /// Normally this should be an internal interface but since it should be available for + /// the aggregator it must be made public. + [ComVisible(true)] + public interface IReferenceContainerProvider + { + IReferenceContainer GetReferenceContainer(); + } + + /// + /// Defines a container for manipulating references + /// + /// Normally this should be an internal interface but since it should be available for + /// the aggregator it must be made public. + [ComVisible(true)] + public interface IReferenceContainer + { + IList EnumReferences(); + ReferenceNode AddReferenceFromSelectorData(VSCOMPONENTSELECTORDATA selectorData); + void LoadReferencesFromBuildProject(MSBuild.Project buildProject); + } + + /// + /// Defines the events that are internally defined for communication with other subsytems. + /// + [ComVisible(true)] + public interface IProjectEvents + { + /// + /// Event raised just after the project file opened. + /// + [SuppressMessage("Microsoft.Naming", "CA1713:EventsShouldNotHaveBeforeOrAfterPrefix")] + event EventHandler AfterProjectFileOpened; + + /// + /// Event raised before the project file closed. + /// + [SuppressMessage("Microsoft.Naming", "CA1713:EventsShouldNotHaveBeforeOrAfterPrefix")] + event EventHandler BeforeProjectFileClosed; + } + + /// + /// Defines the interface that will specify ehethrr the object is a project events listener. + /// + [ComVisible(true)] + public interface IProjectEventsListener + { + + /// + /// Is the object a project events listener. + /// + /// + bool IsProjectEventsListener + { get; set; } + + } + + /// + /// Enable getting and setting the project events provider + /// + [ComVisible(true)] + public interface IProjectEventsProvider + { + /// + /// Defines the provider for the project events + /// + IProjectEvents ProjectEventsProvider + { + get; + set; + } + } + + /// + /// Defines support for single file generator + /// + public interface ISingleFileGenerator + { + /// + /// Runs the generator on the item represented by the document moniker. + /// + /// + void RunGenerator(string document); + } +} \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Key.snk b/Tools/IronStudio/IronStudio/VisualStudio/Project/Key.snk new file mode 100644 index 0000000000..f55b44f357 Binary files /dev/null and b/Tools/IronStudio/IronStudio/VisualStudio/Project/Key.snk differ diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/LocalizableProperties.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/LocalizableProperties.cs new file mode 100644 index 0000000000..11c571fab1 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/LocalizableProperties.cs @@ -0,0 +1,111 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.ComponentModel; +using System.Runtime.InteropServices; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Enables a managed object to expose properties and attributes for COM objects. + /// + [ComVisible(true)] + public class LocalizableProperties : ICustomTypeDescriptor + { + #region ICustomTypeDescriptor + public virtual AttributeCollection GetAttributes() + { + AttributeCollection col = TypeDescriptor.GetAttributes(this, true); + return col; + } + + public virtual EventDescriptor GetDefaultEvent() + { + EventDescriptor ed = TypeDescriptor.GetDefaultEvent(this, true); + return ed; + } + + public virtual PropertyDescriptor GetDefaultProperty() + { + PropertyDescriptor pd = TypeDescriptor.GetDefaultProperty(this, true); + return pd; + } + + public virtual object GetEditor(Type editorBaseType) + { + object o = TypeDescriptor.GetEditor(this, editorBaseType, true); + return o; + } + + public virtual EventDescriptorCollection GetEvents() + { + EventDescriptorCollection edc = TypeDescriptor.GetEvents(this, true); + return edc; + } + + public virtual EventDescriptorCollection GetEvents(System.Attribute[] attributes) + { + EventDescriptorCollection edc = TypeDescriptor.GetEvents(this, attributes, true); + return edc; + } + + public virtual object GetPropertyOwner(PropertyDescriptor pd) + { + return this; + } + + public virtual PropertyDescriptorCollection GetProperties() + { + PropertyDescriptorCollection pcol = GetProperties(null); + return pcol; + } + + public virtual PropertyDescriptorCollection GetProperties(System.Attribute[] attributes) + { + ArrayList newList = new ArrayList(); + PropertyDescriptorCollection props = TypeDescriptor.GetProperties(this, attributes, true); + + for(int i = 0; i < props.Count; i++) + newList.Add(CreateDesignPropertyDescriptor(props[i])); + + return new PropertyDescriptorCollection((PropertyDescriptor[])newList.ToArray(typeof(PropertyDescriptor))); ; + } + + public virtual DesignPropertyDescriptor CreateDesignPropertyDescriptor(PropertyDescriptor propertyDescriptor) + { + return new DesignPropertyDescriptor(propertyDescriptor); + } + + public virtual string GetComponentName() + { + string name = TypeDescriptor.GetComponentName(this, true); + return name; + } + + public virtual TypeConverter GetConverter() + { + TypeConverter tc = TypeDescriptor.GetConverter(this, true); + return tc; + } + + public virtual string GetClassName() + { + return this.GetType().FullName; + } + + #endregion ICustomTypeDescriptor + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/MPFProjectAll.files b/Tools/IronStudio/IronStudio/VisualStudio/Project/MPFProjectAll.files new file mode 100644 index 0000000000..96f05a08ec --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/MPFProjectAll.files @@ -0,0 +1,11 @@ + + + + + . + + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Microsoft.VisualStudio.Project.csproj b/Tools/IronStudio/IronStudio/VisualStudio/Project/Microsoft.VisualStudio.Project.csproj new file mode 100644 index 0000000000..0f7787dae9 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Microsoft.VisualStudio.Project.csproj @@ -0,0 +1,228 @@ + + + + Debug + AnyCPU + 10.0.20506 + 2.0 + {CACB60A9-1E76-4F92-8831-B134A658C695} + Library + Properties + Microsoft.VisualStudio.Project + Microsoft.VisualStudio.Project + v4.0 + 512 + true + Key.snk + + + + + + + + + $(AllowedAssemblyPrefix);Microsoft.Internal + + + true + full + false + $(SolutionDir)..\bin\Debug + DEBUG;TRACE + prompt + 4 + true + AllRules.ruleset + + + pdbonly + true + $(SolutionDir)..\bin\Release + TRACE + prompt + 4 + + + + False + $(MSBuildExtensionsPath)\..\Microsoft Visual Studio 9.0\Common7\IDE\PublicAssemblies\EnvDTE.dll + + + True + + + + True + + + 4.0 + + + + 4.0 + + + True + + + + + + + False + + + False + + + False + + + False + + + + + + 3.5 + + + + + + + + + + True + + + False + $(MSBuildExtensionsPath)\..\Program Files\Common Files\Microsoft Shared\MSEnv\PublicAssemblies\VSLangProj80.dll + + + True + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + Microsoft.VisualStudio.Project.resources + Designer + + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Misc/ConnectionPointContainer.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Misc/ConnectionPointContainer.cs new file mode 100644 index 0000000000..f99601cf96 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Misc/ConnectionPointContainer.cs @@ -0,0 +1,128 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Class used to identify a source of events of type SinkType. + /// + [ComVisible(false)] + internal interface IEventSource + where SinkType : class + { + void OnSinkAdded(SinkType sink); + void OnSinkRemoved(SinkType sink); + } + + [ComVisible(true)] + public class ConnectionPointContainer : IConnectionPointContainer + { + private Dictionary connectionPoints; + internal ConnectionPointContainer() + { + connectionPoints = new Dictionary(); + } + internal void AddEventSource(IEventSource source) + where SinkType : class + { + if(null == source) + { + throw new ArgumentNullException("source"); + } + if(connectionPoints.ContainsKey(typeof(SinkType).GUID)) + { + throw new ArgumentException("EventSource guid already added to the list of connection points", "source"); + } + connectionPoints.Add(typeof(SinkType).GUID, new ConnectionPoint(this, source)); + } + + #region IConnectionPointContainer Members + void IConnectionPointContainer.EnumConnectionPoints(out IEnumConnectionPoints ppEnum) + { + throw new NotImplementedException(); ; + } + void IConnectionPointContainer.FindConnectionPoint(ref Guid riid, out IConnectionPoint ppCP) + { + ppCP = connectionPoints[riid]; + } + #endregion + } + + internal class ConnectionPoint : IConnectionPoint + where SinkType : class + { + Dictionary sinks; + private uint nextCookie; + private ConnectionPointContainer container; + private IEventSource source; + internal ConnectionPoint(ConnectionPointContainer container, IEventSource source) + { + if(null == container) + { + throw new ArgumentNullException("container"); + } + if(null == source) + { + throw new ArgumentNullException("source"); + } + this.container = container; + this.source = source; + sinks = new Dictionary(); + nextCookie = 1; + } + #region IConnectionPoint Members + public void Advise(object pUnkSink, out uint pdwCookie) + { + SinkType sink = pUnkSink as SinkType; + if(null == sink) + { + Marshal.ThrowExceptionForHR(VSConstants.E_NOINTERFACE); + } + sinks.Add(nextCookie, sink); + pdwCookie = nextCookie; + source.OnSinkAdded(sink); + nextCookie += 1; + } + + public void EnumConnections(out IEnumConnections ppEnum) + { + throw new NotImplementedException(); ; + } + + public void GetConnectionInterface(out Guid pIID) + { + pIID = typeof(SinkType).GUID; + } + + public void GetConnectionPointContainer(out IConnectionPointContainer ppCPC) + { + ppCPC = this.container; + } + + public void Unadvise(uint dwCookie) + { + // This will throw if the cookie is not in the list. + SinkType sink = sinks[dwCookie]; + sinks.Remove(dwCookie); + source.OnSinkRemoved(sink); + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Misc/ExternDll.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Misc/ExternDll.cs new file mode 100644 index 0000000000..383a90974c --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Misc/ExternDll.cs @@ -0,0 +1,88 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +namespace Microsoft.VisualStudio.Project +{ + internal static class ExternDll + { + +#if FEATURE_PAL + +#if !PLATFORM_UNIX + internal const String DLLPREFIX = ""; + internal const String DLLSUFFIX = ".dll"; +#else // !PLATFORM_UNIX +#if __APPLE__ + internal const String DLLPREFIX = "lib"; + internal const String DLLSUFFIX = ".dylib"; +#elif _AIX + internal const String DLLPREFIX = "lib"; + internal const String DLLSUFFIX = ".a"; +#elif __hppa__ || IA64 + internal const String DLLPREFIX = "lib"; + internal const String DLLSUFFIX = ".sl"; +#else + internal const String DLLPREFIX = "lib"; + internal const String DLLSUFFIX = ".so"; +#endif +#endif // !PLATFORM_UNIX + + public const string Kernel32 = DLLPREFIX + "rotor_pal" + DLLSUFFIX; + public const string User32 = DLLPREFIX + "rotor_pal" + DLLSUFFIX; + public const string Mscoree = DLLPREFIX + "sscoree" + DLLSUFFIX; +#else + public const string Activeds = "activeds.dll"; + public const string Advapi32 = "advapi32.dll"; + public const string Comctl32 = "comctl32.dll"; + public const string Comdlg32 = "comdlg32.dll"; + public const string Gdi32 = "gdi32.dll"; + public const string Gdiplus = "gdiplus.dll"; + public const string Hhctrl = "hhctrl.ocx"; + public const string Imm32 = "imm32.dll"; + public const string Kernel32 = "kernel32.dll"; + public const string Loadperf = "Loadperf.dll"; + public const string Mscoree = "mscoree.dll"; + public const string Mscorwks = "mscorwks.dll"; + public const string Msi = "msi.dll"; + public const string Mqrt = "mqrt.dll"; + public const string Ntdll = "ntdll.dll"; + public const string Ole32 = "ole32.dll"; + public const string Oleacc = "oleacc.dll"; + public const string Oleaut32 = "oleaut32.dll"; + public const string Olepro32 = "olepro32.dll"; + public const string PerfCounter = "perfcounter.dll"; + public const string Powrprof = "Powrprof.dll"; + public const string Psapi = "psapi.dll"; + public const string Shell32 = "shell32.dll"; + public const string Shfolder = "shfolder.dll"; + public const string User32 = "user32.dll"; + public const string Uxtheme = "uxtheme.dll"; + public const string WinMM = "winmm.dll"; + public const string Winspool = "winspool.drv"; + public const string Wtsapi32 = "wtsapi32.dll"; + public const string Version = "version.dll"; + public const string Vsassert = "vsassert.dll"; + public const string Shlwapi = "shlwapi.dll"; + public const string Crypt32 = "crypt32.dll"; + + // system.data specific + internal const string Odbc32 = "odbc32.dll"; + internal const string SNI = "System.Data.dll"; + + // system.data.oracleclient specific + internal const string OciDll = "oci.dll"; + internal const string OraMtsDll = "oramts.dll"; +#endif //!FEATURE_PAL + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Misc/NativeMethods.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Misc/NativeMethods.cs new file mode 100644 index 0000000000..700ffb4b18 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Misc/NativeMethods.cs @@ -0,0 +1,710 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.IO; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.TextManager.Interop; + +namespace Microsoft.VisualStudio.Project +{ + internal static class NativeMethods + { + // IIDS + public static readonly Guid IID_IUnknown = new Guid("{00000000-0000-0000-C000-000000000046}"); + + public const int + CLSCTX_INPROC_SERVER = 0x1; + + public const int + S_FALSE = 0x00000001, + S_OK = 0x00000000, + + IDOK = 1, + IDCANCEL = 2, + IDABORT = 3, + IDRETRY = 4, + IDIGNORE = 5, + IDYES = 6, + IDNO = 7, + IDCLOSE = 8, + IDHELP = 9, + IDTRYAGAIN = 10, + IDCONTINUE = 11, + + OLECMDERR_E_NOTSUPPORTED = unchecked((int)0x80040100), + OLECMDERR_E_UNKNOWNGROUP = unchecked((int)0x80040104), + + UNDO_E_CLIENTABORT = unchecked((int)0x80044001), + E_OUTOFMEMORY = unchecked((int)0x8007000E), + E_INVALIDARG = unchecked((int)0x80070057), + E_FAIL = unchecked((int)0x80004005), + E_NOINTERFACE = unchecked((int)0x80004002), + E_POINTER = unchecked((int)0x80004003), + E_NOTIMPL = unchecked((int)0x80004001), + E_UNEXPECTED = unchecked((int)0x8000FFFF), + E_HANDLE = unchecked((int)0x80070006), + E_ABORT = unchecked((int)0x80004004), + E_ACCESSDENIED = unchecked((int)0x80070005), + E_PENDING = unchecked((int)0x8000000A); + + public const int + OLECLOSE_SAVEIFDIRTY = 0, + OLECLOSE_NOSAVE = 1, + OLECLOSE_PROMPTSAVE = 2; + + public const int + OLEIVERB_PRIMARY = 0, + OLEIVERB_SHOW = -1, + OLEIVERB_OPEN = -2, + OLEIVERB_HIDE = -3, + OLEIVERB_UIACTIVATE = -4, + OLEIVERB_INPLACEACTIVATE = -5, + OLEIVERB_DISCARDUNDOSTATE = -6, + OLEIVERB_PROPERTIES = -7; + + public const int + OFN_READONLY = unchecked((int)0x00000001), + OFN_OVERWRITEPROMPT = unchecked((int)0x00000002), + OFN_HIDEREADONLY = unchecked((int)0x00000004), + OFN_NOCHANGEDIR = unchecked((int)0x00000008), + OFN_SHOWHELP = unchecked((int)0x00000010), + OFN_ENABLEHOOK = unchecked((int)0x00000020), + OFN_ENABLETEMPLATE = unchecked((int)0x00000040), + OFN_ENABLETEMPLATEHANDLE = unchecked((int)0x00000080), + OFN_NOVALIDATE = unchecked((int)0x00000100), + OFN_ALLOWMULTISELECT = unchecked((int)0x00000200), + OFN_EXTENSIONDIFFERENT = unchecked((int)0x00000400), + OFN_PATHMUSTEXIST = unchecked((int)0x00000800), + OFN_FILEMUSTEXIST = unchecked((int)0x00001000), + OFN_CREATEPROMPT = unchecked((int)0x00002000), + OFN_SHAREAWARE = unchecked((int)0x00004000), + OFN_NOREADONLYRETURN = unchecked((int)0x00008000), + OFN_NOTESTFILECREATE = unchecked((int)0x00010000), + OFN_NONETWORKBUTTON = unchecked((int)0x00020000), + OFN_NOLONGNAMES = unchecked((int)0x00040000), + OFN_EXPLORER = unchecked((int)0x00080000), + OFN_NODEREFERENCELINKS = unchecked((int)0x00100000), + OFN_LONGNAMES = unchecked((int)0x00200000), + OFN_ENABLEINCLUDENOTIFY = unchecked((int)0x00400000), + OFN_ENABLESIZING = unchecked((int)0x00800000), + OFN_USESHELLITEM = unchecked((int)0x01000000), + OFN_DONTADDTORECENT = unchecked((int)0x02000000), + OFN_FORCESHOWHIDDEN = unchecked((int)0x10000000); + + // for READONLYSTATUS + public const int + ROSTATUS_NotReadOnly = 0x0, + ROSTATUS_ReadOnly = 0x1, + ROSTATUS_Unknown = unchecked((int)0xFFFFFFFF); + + public const int + IEI_DoNotLoadDocData = 0x10000000; + + public const int + CB_SETDROPPEDWIDTH = 0x0160, + + GWL_STYLE = (-16), + GWL_EXSTYLE = (-20), + + DWL_MSGRESULT = 0, + + SW_SHOWNORMAL = 1, + + HTMENU = 5, + + WS_POPUP = unchecked((int)0x80000000), + WS_CHILD = 0x40000000, + WS_MINIMIZE = 0x20000000, + WS_VISIBLE = 0x10000000, + WS_DISABLED = 0x08000000, + WS_CLIPSIBLINGS = 0x04000000, + WS_CLIPCHILDREN = 0x02000000, + WS_MAXIMIZE = 0x01000000, + WS_CAPTION = 0x00C00000, + WS_BORDER = 0x00800000, + WS_DLGFRAME = 0x00400000, + WS_VSCROLL = 0x00200000, + WS_HSCROLL = 0x00100000, + WS_SYSMENU = 0x00080000, + WS_THICKFRAME = 0x00040000, + WS_TABSTOP = 0x00010000, + WS_MINIMIZEBOX = 0x00020000, + WS_MAXIMIZEBOX = 0x00010000, + WS_EX_DLGMODALFRAME = 0x00000001, + WS_EX_MDICHILD = 0x00000040, + WS_EX_TOOLWINDOW = 0x00000080, + WS_EX_CLIENTEDGE = 0x00000200, + WS_EX_CONTEXTHELP = 0x00000400, + WS_EX_RIGHT = 0x00001000, + WS_EX_LEFT = 0x00000000, + WS_EX_RTLREADING = 0x00002000, + WS_EX_LEFTSCROLLBAR = 0x00004000, + WS_EX_CONTROLPARENT = 0x00010000, + WS_EX_STATICEDGE = 0x00020000, + WS_EX_APPWINDOW = 0x00040000, + WS_EX_LAYERED = 0x00080000, + WS_EX_TOPMOST = 0x00000008, + WS_EX_NOPARENTNOTIFY = 0x00000004, + + LVM_SETEXTENDEDLISTVIEWSTYLE = (0x1000 + 54), + + LVS_EX_LABELTIP = 0x00004000, + + // winuser.h + WH_JOURNALPLAYBACK = 1, + WH_GETMESSAGE = 3, + WH_MOUSE = 7, + WSF_VISIBLE = 0x0001, + WM_NULL = 0x0000, + WM_CREATE = 0x0001, + WM_DELETEITEM = 0x002D, + WM_DESTROY = 0x0002, + WM_MOVE = 0x0003, + WM_SIZE = 0x0005, + WM_ACTIVATE = 0x0006, + WA_INACTIVE = 0, + WA_ACTIVE = 1, + WA_CLICKACTIVE = 2, + WM_SETFOCUS = 0x0007, + WM_KILLFOCUS = 0x0008, + WM_ENABLE = 0x000A, + WM_SETREDRAW = 0x000B, + WM_SETTEXT = 0x000C, + WM_GETTEXT = 0x000D, + WM_GETTEXTLENGTH = 0x000E, + WM_PAINT = 0x000F, + WM_CLOSE = 0x0010, + WM_QUERYENDSESSION = 0x0011, + WM_QUIT = 0x0012, + WM_QUERYOPEN = 0x0013, + WM_ERASEBKGND = 0x0014, + WM_SYSCOLORCHANGE = 0x0015, + WM_ENDSESSION = 0x0016, + WM_SHOWWINDOW = 0x0018, + WM_WININICHANGE = 0x001A, + WM_SETTINGCHANGE = 0x001A, + WM_DEVMODECHANGE = 0x001B, + WM_ACTIVATEAPP = 0x001C, + WM_FONTCHANGE = 0x001D, + WM_TIMECHANGE = 0x001E, + WM_CANCELMODE = 0x001F, + WM_SETCURSOR = 0x0020, + WM_MOUSEACTIVATE = 0x0021, + WM_CHILDACTIVATE = 0x0022, + WM_QUEUESYNC = 0x0023, + WM_GETMINMAXINFO = 0x0024, + WM_PAINTICON = 0x0026, + WM_ICONERASEBKGND = 0x0027, + WM_NEXTDLGCTL = 0x0028, + WM_SPOOLERSTATUS = 0x002A, + WM_DRAWITEM = 0x002B, + WM_MEASUREITEM = 0x002C, + WM_VKEYTOITEM = 0x002E, + WM_CHARTOITEM = 0x002F, + WM_SETFONT = 0x0030, + WM_GETFONT = 0x0031, + WM_SETHOTKEY = 0x0032, + WM_GETHOTKEY = 0x0033, + WM_QUERYDRAGICON = 0x0037, + WM_COMPAREITEM = 0x0039, + WM_GETOBJECT = 0x003D, + WM_COMPACTING = 0x0041, + WM_COMMNOTIFY = 0x0044, + WM_WINDOWPOSCHANGING = 0x0046, + WM_WINDOWPOSCHANGED = 0x0047, + WM_POWER = 0x0048, + WM_COPYDATA = 0x004A, + WM_CANCELJOURNAL = 0x004B, + WM_NOTIFY = 0x004E, + WM_INPUTLANGCHANGEREQUEST = 0x0050, + WM_INPUTLANGCHANGE = 0x0051, + WM_TCARD = 0x0052, + WM_HELP = 0x0053, + WM_USERCHANGED = 0x0054, + WM_NOTIFYFORMAT = 0x0055, + WM_CONTEXTMENU = 0x007B, + WM_STYLECHANGING = 0x007C, + WM_STYLECHANGED = 0x007D, + WM_DISPLAYCHANGE = 0x007E, + WM_GETICON = 0x007F, + WM_SETICON = 0x0080, + WM_NCCREATE = 0x0081, + WM_NCDESTROY = 0x0082, + WM_NCCALCSIZE = 0x0083, + WM_NCHITTEST = 0x0084, + WM_NCPAINT = 0x0085, + WM_NCACTIVATE = 0x0086, + WM_GETDLGCODE = 0x0087, + WM_NCMOUSEMOVE = 0x00A0, + WM_NCLBUTTONDOWN = 0x00A1, + WM_NCLBUTTONUP = 0x00A2, + WM_NCLBUTTONDBLCLK = 0x00A3, + WM_NCRBUTTONDOWN = 0x00A4, + WM_NCRBUTTONUP = 0x00A5, + WM_NCRBUTTONDBLCLK = 0x00A6, + WM_NCMBUTTONDOWN = 0x00A7, + WM_NCMBUTTONUP = 0x00A8, + WM_NCMBUTTONDBLCLK = 0x00A9, + WM_NCXBUTTONDOWN = 0x00AB, + WM_NCXBUTTONUP = 0x00AC, + WM_NCXBUTTONDBLCLK = 0x00AD, + WM_KEYFIRST = 0x0100, + WM_KEYDOWN = 0x0100, + WM_KEYUP = 0x0101, + WM_CHAR = 0x0102, + WM_DEADCHAR = 0x0103, + WM_CTLCOLOR = 0x0019, + WM_SYSKEYDOWN = 0x0104, + WM_SYSKEYUP = 0x0105, + WM_SYSCHAR = 0x0106, + WM_SYSDEADCHAR = 0x0107, + WM_KEYLAST = 0x0108, + WM_IME_STARTCOMPOSITION = 0x010D, + WM_IME_ENDCOMPOSITION = 0x010E, + WM_IME_COMPOSITION = 0x010F, + WM_IME_KEYLAST = 0x010F, + WM_INITDIALOG = 0x0110, + WM_COMMAND = 0x0111, + WM_SYSCOMMAND = 0x0112, + WM_TIMER = 0x0113, + WM_HSCROLL = 0x0114, + WM_VSCROLL = 0x0115, + WM_INITMENU = 0x0116, + WM_INITMENUPOPUP = 0x0117, + WM_MENUSELECT = 0x011F, + WM_MENUCHAR = 0x0120, + WM_ENTERIDLE = 0x0121, + WM_CHANGEUISTATE = 0x0127, + WM_UPDATEUISTATE = 0x0128, + WM_QUERYUISTATE = 0x0129, + WM_CTLCOLORMSGBOX = 0x0132, + WM_CTLCOLOREDIT = 0x0133, + WM_CTLCOLORLISTBOX = 0x0134, + WM_CTLCOLORBTN = 0x0135, + WM_CTLCOLORDLG = 0x0136, + WM_CTLCOLORSCROLLBAR = 0x0137, + WM_CTLCOLORSTATIC = 0x0138, + WM_MOUSEFIRST = 0x0200, + WM_MOUSEMOVE = 0x0200, + WM_LBUTTONDOWN = 0x0201, + WM_LBUTTONUP = 0x0202, + WM_LBUTTONDBLCLK = 0x0203, + WM_RBUTTONDOWN = 0x0204, + WM_RBUTTONUP = 0x0205, + WM_RBUTTONDBLCLK = 0x0206, + WM_MBUTTONDOWN = 0x0207, + WM_MBUTTONUP = 0x0208, + WM_MBUTTONDBLCLK = 0x0209, + WM_XBUTTONDOWN = 0x020B, + WM_XBUTTONUP = 0x020C, + WM_XBUTTONDBLCLK = 0x020D, + WM_MOUSEWHEEL = 0x020A, + WM_MOUSELAST = 0x020A, + WM_PARENTNOTIFY = 0x0210, + WM_ENTERMENULOOP = 0x0211, + WM_EXITMENULOOP = 0x0212, + WM_NEXTMENU = 0x0213, + WM_SIZING = 0x0214, + WM_CAPTURECHANGED = 0x0215, + WM_MOVING = 0x0216, + WM_POWERBROADCAST = 0x0218, + WM_DEVICECHANGE = 0x0219, + WM_IME_SETCONTEXT = 0x0281, + WM_IME_NOTIFY = 0x0282, + WM_IME_CONTROL = 0x0283, + WM_IME_COMPOSITIONFULL = 0x0284, + WM_IME_SELECT = 0x0285, + WM_IME_CHAR = 0x0286, + WM_IME_KEYDOWN = 0x0290, + WM_IME_KEYUP = 0x0291, + WM_MDICREATE = 0x0220, + WM_MDIDESTROY = 0x0221, + WM_MDIACTIVATE = 0x0222, + WM_MDIRESTORE = 0x0223, + WM_MDINEXT = 0x0224, + WM_MDIMAXIMIZE = 0x0225, + WM_MDITILE = 0x0226, + WM_MDICASCADE = 0x0227, + WM_MDIICONARRANGE = 0x0228, + WM_MDIGETACTIVE = 0x0229, + WM_MDISETMENU = 0x0230, + WM_ENTERSIZEMOVE = 0x0231, + WM_EXITSIZEMOVE = 0x0232, + WM_DROPFILES = 0x0233, + WM_MDIREFRESHMENU = 0x0234, + WM_MOUSEHOVER = 0x02A1, + WM_MOUSELEAVE = 0x02A3, + WM_CUT = 0x0300, + WM_COPY = 0x0301, + WM_PASTE = 0x0302, + WM_CLEAR = 0x0303, + WM_UNDO = 0x0304, + WM_RENDERFORMAT = 0x0305, + WM_RENDERALLFORMATS = 0x0306, + WM_DESTROYCLIPBOARD = 0x0307, + WM_DRAWCLIPBOARD = 0x0308, + WM_PAINTCLIPBOARD = 0x0309, + WM_VSCROLLCLIPBOARD = 0x030A, + WM_SIZECLIPBOARD = 0x030B, + WM_ASKCBFORMATNAME = 0x030C, + WM_CHANGECBCHAIN = 0x030D, + WM_HSCROLLCLIPBOARD = 0x030E, + WM_QUERYNEWPALETTE = 0x030F, + WM_PALETTEISCHANGING = 0x0310, + WM_PALETTECHANGED = 0x0311, + WM_HOTKEY = 0x0312, + WM_PRINT = 0x0317, + WM_PRINTCLIENT = 0x0318, + WM_HANDHELDFIRST = 0x0358, + WM_HANDHELDLAST = 0x035F, + WM_AFXFIRST = 0x0360, + WM_AFXLAST = 0x037F, + WM_PENWINFIRST = 0x0380, + WM_PENWINLAST = 0x038F, + WM_APP = unchecked((int)0x8000), + WM_USER = 0x0400, + WM_REFLECT = + WM_USER + 0x1C00, + WS_OVERLAPPED = 0x00000000, + WPF_SETMINPOSITION = 0x0001, + WM_CHOOSEFONT_GETLOGFONT = (0x0400 + 1), + + WHEEL_DELTA = 120, + DWLP_MSGRESULT = 0, + PSNRET_NOERROR = 0, + PSNRET_INVALID = 1, + PSNRET_INVALID_NOCHANGEPAGE = 2; + + public const int + PSN_APPLY = ((0 - 200) - 2), + PSN_KILLACTIVE = ((0 - 200) - 1), + PSN_RESET = ((0 - 200) - 3), + PSN_SETACTIVE = ((0 - 200) - 0); + + public const int + GMEM_MOVEABLE = 0x0002, + GMEM_ZEROINIT = 0x0040, + GMEM_DDESHARE = 0x2000; + + public const int + SWP_NOACTIVATE = 0x0010, + SWP_NOZORDER = 0x0004, + SWP_NOSIZE = 0x0001, + SWP_NOMOVE = 0x0002, + SWP_FRAMECHANGED = 0x0020; + + public const int + TVM_SETINSERTMARK = (0x1100 + 26), + TVM_GETEDITCONTROL = (0x1100 + 15); + + public const int + FILE_ATTRIBUTE_READONLY = 0x00000001; + + public const int + PSP_DEFAULT = 0x00000000, + PSP_DLGINDIRECT = 0x00000001, + PSP_USEHICON = 0x00000002, + PSP_USEICONID = 0x00000004, + PSP_USETITLE = 0x00000008, + PSP_RTLREADING = 0x00000010, + PSP_HASHELP = 0x00000020, + PSP_USEREFPARENT = 0x00000040, + PSP_USECALLBACK = 0x00000080, + PSP_PREMATURE = 0x00000400, + PSP_HIDEHEADER = 0x00000800, + PSP_USEHEADERTITLE = 0x00001000, + PSP_USEHEADERSUBTITLE = 0x00002000; + + public const int + PSH_DEFAULT = 0x00000000, + PSH_PROPTITLE = 0x00000001, + PSH_USEHICON = 0x00000002, + PSH_USEICONID = 0x00000004, + PSH_PROPSHEETPAGE = 0x00000008, + PSH_WIZARDHASFINISH = 0x00000010, + PSH_WIZARD = 0x00000020, + PSH_USEPSTARTPAGE = 0x00000040, + PSH_NOAPPLYNOW = 0x00000080, + PSH_USECALLBACK = 0x00000100, + PSH_HASHELP = 0x00000200, + PSH_MODELESS = 0x00000400, + PSH_RTLREADING = 0x00000800, + PSH_WIZARDCONTEXTHELP = 0x00001000, + PSH_WATERMARK = 0x00008000, + PSH_USEHBMWATERMARK = 0x00010000, // user pass in a hbmWatermark instead of pszbmWatermark + PSH_USEHPLWATERMARK = 0x00020000, // + PSH_STRETCHWATERMARK = 0x00040000, // stretchwatermark also applies for the header + PSH_HEADER = 0x00080000, + PSH_USEHBMHEADER = 0x00100000, + PSH_USEPAGELANG = 0x00200000, // use frame dialog template matched to page + PSH_WIZARD_LITE = 0x00400000, + PSH_NOCONTEXTHELP = 0x02000000; + + public const int + PSBTN_BACK = 0, + PSBTN_NEXT = 1, + PSBTN_FINISH = 2, + PSBTN_OK = 3, + PSBTN_APPLYNOW = 4, + PSBTN_CANCEL = 5, + PSBTN_HELP = 6, + PSBTN_MAX = 6; + + public const int + TRANSPARENT = 1, + OPAQUE = 2, + FW_BOLD = 700; + + [StructLayout(LayoutKind.Sequential)] + public struct NMHDR + { + public IntPtr hwndFrom; + public int idFrom; + public int code; + } + + /// + /// Helper class for setting the text parameters to OLECMDTEXT structures. + /// + public static class OLECMDTEXT + { + + /// + /// Flags for the OLE command text + /// + public enum OLECMDTEXTF + { + /// No flag + OLECMDTEXTF_NONE = 0, + /// The name of the command is required. + OLECMDTEXTF_NAME = 1, + /// A description of the status is required. + OLECMDTEXTF_STATUS = 2 + } + } + + /// + /// OLECMDF enums for IOleCommandTarget + /// + public enum tagOLECMDF + { + OLECMDF_SUPPORTED = 1, + OLECMDF_ENABLED = 2, + OLECMDF_LATCHED = 4, + OLECMDF_NINCHED = 8, + OLECMDF_INVISIBLE = 16 + } + + /// + /// This method takes a file URL and converts it to an absolute path. The trick here is that + /// if there is a '#' in the path, everything after this is treated as a fragment. So + /// we need to append the fragment to the end of the path. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static string GetAbsolutePath(string fileName) + { + System.Diagnostics.Debug.Assert(fileName != null && fileName.Length > 0, "Cannot get absolute path, fileName is not valid"); + + Uri uri = new Uri(fileName); + return uri.LocalPath + uri.Fragment; + } + + /// + /// Please use this "approved" method to compare file names. + /// + public static bool IsSamePath(string file1, string file2) + { + if(file1 == null || file1.Length == 0) + { + return (file2 == null || file2.Length == 0); + } + + Uri uri1 = null; + Uri uri2 = null; + + try + { + if(!Uri.TryCreate(file1, UriKind.Absolute, out uri1) || !Uri.TryCreate(file2, UriKind.Absolute, out uri2)) + { + return false; + } + + if(uri1 != null && uri1.IsFile && uri2 != null && uri2.IsFile) + { + return 0 == String.Compare(uri1.LocalPath, uri2.LocalPath, StringComparison.OrdinalIgnoreCase); + } + + return file1 == file2; + } + catch(UriFormatException e) + { + Trace.WriteLine("Exception " + e.Message); + } + + return false; + } + + [ComImport(), Guid("9BDA66AE-CA28-4e22-AA27-8A7218A0E3FA"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IEventHandler + { + + // converts the underlying codefunction into an event handler for the given event + // if the given event is NULL, then the function will handle no events + [PreserveSig] + int AddHandler(string bstrEventName); + + [PreserveSig] + int RemoveHandler(string bstrEventName); + + IVsEnumBSTR GetHandledEvents(); + + bool HandlesEvent(string bstrEventName); + } + + [ComImport(), Guid("A55CCBCC-7031-432d-B30A-A68DE7BDAD75"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IParameterKind + { + + void SetParameterPassingMode(PARAMETER_PASSING_MODE ParamPassingMode); + void SetParameterArrayDimensions(int uDimensions); + int GetParameterArrayCount(); + int GetParameterArrayDimensions(int uIndex); + int GetParameterPassingMode(); + } + + public enum PARAMETER_PASSING_MODE + { + cmParameterTypeIn = 1, + cmParameterTypeOut = 2, + cmParameterTypeInOut = 3 + } + + [ + ComImport, ComVisible(true), Guid("3E596484-D2E4-461a-A876-254C4F097EBB"), + InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown) + ] + public interface IMethodXML + { + // Generate XML describing the contents of this function's body. + void GetXML(ref string pbstrXML); + + // Parse the incoming XML with respect to the CodeModel XML schema and + // use the result to regenerate the body of the function. + /// + [PreserveSig] + int SetXML(string pszXML); + + // This is really a textpoint + [PreserveSig] + int GetBodyPoint([MarshalAs(UnmanagedType.Interface)]out object bodyPoint); + } + + [ComImport(), Guid("EA1A87AD-7BC5-4349-B3BE-CADC301F17A3"), InterfaceTypeAttribute(ComInterfaceType.InterfaceIsIUnknown)] + public interface IVBFileCodeModelEvents + { + + [PreserveSig] + int StartEdit(); + + [PreserveSig] + int EndEdit(); + } + + ///-------------------------------------------------------------------------- + /// ICodeClassBase: + ///-------------------------------------------------------------------------- + [GuidAttribute("23BBD58A-7C59-449b-A93C-43E59EFC080C")] + [InterfaceType(ComInterfaceType.InterfaceIsIUnknown)] + [ComImport()] + public interface ICodeClassBase + { + [PreserveSig()] + int GetBaseName(out string pBaseName); + } + + public const ushort CF_HDROP = 15; // winuser.h + public const uint MK_CONTROL = 0x0008; //winuser.h + public const uint MK_SHIFT = 0x0004; + public const int MAX_PATH = 260; // windef.h + + /// + /// Specifies options for a bitmap image associated with a task item. + /// + public enum VSTASKBITMAP + { + BMP_COMPILE = -1, + BMP_SQUIGGLE = -2, + BMP_COMMENT = -3, + BMP_SHORTCUT = -4, + BMP_USER = -5 + }; + + public const int ILD_NORMAL = 0x0000, + ILD_TRANSPARENT = 0x0001, + ILD_MASK = 0x0010, + ILD_ROP = 0x0040; + + /// + /// Defines the values that are not supported by the System.Environment.SpecialFolder enumeration + /// + [ComVisible(true)] + public enum ExtendedSpecialFolder + { + /// + /// Identical to CSIDL_COMMON_STARTUP + /// + CommonStartup = 0x0018, + + /// + /// Identical to CSIDL_WINDOWS + /// + Windows = 0x0024, + } + + + // APIS + + /// + /// Changes the parent window of the specified child window. + /// + /// Handle to the child window. + /// Handle to the new parent window. If this parameter is NULL, the desktop window becomes the new parent window. + /// A handle to the previous parent window indicates success. NULL indicates failure. + [DllImport("User32", ExactSpelling = true, CharSet = CharSet.Auto)] + public static extern IntPtr SetParent(IntPtr hWnd, IntPtr hWndParent); + + [DllImport("user32.dll", ExactSpelling = true, CharSet = CharSet.Auto)] + public static extern bool DestroyIcon(IntPtr handle); + + [DllImport("user32.dll", EntryPoint = "IsDialogMessageA", SetLastError = true, CharSet = CharSet.Ansi, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] + public static extern bool IsDialogMessageA(IntPtr hDlg, ref MSG msg); + + /// + /// Indicates whether the file type is binary or not + /// + /// Full path to the file to check + /// If file isbianry the bitness of the app is indicated by lpBinaryType value. + /// True if the file is binary false otherwise + [DllImport("kernel32.dll")] + public static extern bool GetBinaryType([MarshalAs(UnmanagedType.LPWStr)]string lpApplicationName, out uint lpBinaryType); + + } +} + diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Misc/UnsafeNativeMethods.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Misc/UnsafeNativeMethods.cs new file mode 100644 index 0000000000..bf915edb85 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Misc/UnsafeNativeMethods.cs @@ -0,0 +1,68 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.InteropServices; + +namespace Microsoft.VisualStudio.Project +{ + internal static class UnsafeNativeMethods + { + [DllImport(ExternDll.Kernel32, EntryPoint = "GlobalLock", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] + internal static extern IntPtr GlobalLock(IntPtr h); + + [DllImport(ExternDll.Kernel32, EntryPoint = "GlobalUnlock", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] + internal static extern bool GlobalUnLock(IntPtr h); + + [DllImport(ExternDll.Kernel32, EntryPoint = "GlobalSize", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] + internal static extern int GlobalSize(IntPtr h); + + [DllImport(ExternDll.Ole32, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int OleSetClipboard(Microsoft.VisualStudio.OLE.Interop.IDataObject dataObject); + + [DllImport(ExternDll.Ole32, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int OleGetClipboard(out Microsoft.VisualStudio.OLE.Interop.IDataObject dataObject); + + [DllImport(ExternDll.Ole32, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int OleFlushClipboard(); + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int OpenClipboard(IntPtr newOwner); + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int EmptyClipboard(); + + [DllImport(ExternDll.User32, ExactSpelling = true, CharSet = CharSet.Unicode)] + internal static extern int CloseClipboard(); + + [DllImport(ExternDll.Comctl32, CharSet = CharSet.Auto)] + internal static extern int ImageList_GetImageCount(HandleRef himl); + + [DllImport(ExternDll.Comctl32, CharSet = CharSet.Auto)] + internal static extern bool ImageList_Draw(HandleRef himl, int i, HandleRef hdcDst, int x, int y, int fStyle); + + [DllImport(ExternDll.Shell32, EntryPoint = "DragQueryFileW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] + internal static extern uint DragQueryFile(IntPtr hDrop, uint iFile, char[] lpszFile, uint cch); + + [DllImport(ExternDll.User32, EntryPoint = "RegisterClipboardFormatW", SetLastError = true, CharSet = CharSet.Unicode, ExactSpelling = true, CallingConvention = CallingConvention.StdCall)] + internal static extern ushort RegisterClipboardFormat(string format); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode"), DllImport(ExternDll.Shell32, EntryPoint = "SHGetSpecialFolderLocation")] + internal static extern int SHGetSpecialFolderLocation(IntPtr hwnd, int csidl, [Out, MarshalAs(UnmanagedType.LPArray)] IntPtr[] ppidl); + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode"), DllImport(ExternDll.Shell32, EntryPoint = "SHGetPathFromIDList", CharSet = System.Runtime.InteropServices.CharSet.Auto)] + internal static extern bool SHGetPathFromIDList(IntPtr pidl, IntPtr pszPath); + } +} + diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/NestedProjectBuildDependency.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/NestedProjectBuildDependency.cs new file mode 100644 index 0000000000..2e56d6f951 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/NestedProjectBuildDependency.cs @@ -0,0 +1,86 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ +using System; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Used for adding a build dependency to nested project (not a real project reference) + /// + public class NestedProjectBuildDependency : IVsBuildDependency + { + IVsHierarchy dependentHierarchy = null; + + #region ctors + [CLSCompliant(false)] + public NestedProjectBuildDependency(IVsHierarchy dependentHierarchy) + { + this.dependentHierarchy = dependentHierarchy; + } + #endregion + + #region IVsBuildDependency methods + public int get_CanonicalName(out string canonicalName) + { + canonicalName = null; + return VSConstants.S_OK; + } + + public int get_Type(out System.Guid guidType) + { + // All our dependencies are build projects + guidType = VSConstants.GUID_VS_DEPTYPE_BUILD_PROJECT; + + return VSConstants.S_OK; + } + + public int get_Description(out string description) + { + description = null; + return VSConstants.S_OK; + } + + [CLSCompliant(false)] + public int get_HelpContext(out uint helpContext) + { + helpContext = 0; + return VSConstants.E_NOTIMPL; + } + + public int get_HelpFile(out string helpFile) + { + helpFile = null; + return VSConstants.E_NOTIMPL; + } + + public int get_MustUpdateBefore(out int mustUpdateBefore) + { + // Must always update dependencies + mustUpdateBefore = 1; + + return VSConstants.S_OK; + } + + public int get_ReferredProject(out object unknownProject) + { + unknownProject = this.dependentHierarchy; + + return (unknownProject == null) ? VSConstants.E_FAIL : VSConstants.S_OK; + } + #endregion + + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/NestedProjectNode.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/NestedProjectNode.cs new file mode 100644 index 0000000000..12b87cfc41 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/NestedProjectNode.cs @@ -0,0 +1,1151 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using ErrorHandler = Microsoft.VisualStudio.ErrorHandler; +using ShellConstants = Microsoft.VisualStudio.Shell.Interop.Constants; + +namespace Microsoft.VisualStudio.Project +{ + [CLSCompliant(false), ComVisible(true)] + public class NestedProjectNode : HierarchyNode, IPropertyNotifySink + { + #region fields + private IVsHierarchy nestedHierarchy; + + Guid projectInstanceGuid = Guid.Empty; + + private string projectName = String.Empty; + + private string projectPath = String.Empty; + + private ImageHandler imageHandler; + + /// + /// Defines an object that will be a mutex for this object for synchronizing thread calls. + /// + private static volatile object Mutex = new object(); + + /// + /// Sets the dispose flag on the object. + /// + private bool isDisposed; + + // A cooike retrieved when advising on property chnanged events. + private uint projectPropertyNotifySinkCookie; + #endregion + + #region properties + internal IVsHierarchy NestedHierarchy + { + get + { + return this.nestedHierarchy; + } + } + #endregion + + #region virtual properties + /// + /// Returns the __VSADDVPFLAGS that will be passed in when calling AddVirtualProjectEx + /// + protected virtual uint VirtualProjectFlags + { + get { return 0; } + } + #endregion + + #region overridden properties + /// + /// The path of the nested project. + /// + public override string Url + { + get + { + return this.projectPath; + } + } + + /// + /// The Caption of the nested project. + /// + public override string Caption + { + get + { + return Path.GetFileNameWithoutExtension(this.projectName); + } + } + + public override Guid ItemTypeGuid + { + get + { + return VSConstants.GUID_ItemType_SubProject; + } + } + + /// + /// Defines whether a node can execute a command if in selection. + /// We do this in order to let the nested project to handle the execution of its own commands. + /// + public override bool CanExecuteCommand + { + get + { + return false; + } + } + + public override int SortPriority + { + get { return DefaultSortOrderNode.NestedProjectNode; } + } + + protected bool IsDisposed + { + get { return this.isDisposed; } + set { this.isDisposed = value; } + } + + + + #endregion + + #region ctor + + protected NestedProjectNode() + { + } + + public NestedProjectNode(ProjectNode root, ProjectElement element) + : base(root, element) + { + this.IsExpanded = true; + } + #endregion + + #region IPropertyNotifySink Members + /// + /// Notifies a sink that the [bindable] property specified by dispID has changed. + /// If dispID is DISPID_UNKNOWN, then multiple properties have changed together. + /// The client (owner of the sink) should then retrieve the current value of each property of interest from the object that generated the notification. + /// In our case we will care about the VSLangProj80.VsProjPropId.VBPROJPROPID_FileName and update the changes in the parent project file. + /// + /// Dispatch identifier of the property that is about to change or DISPID_UNKNOWN if multiple properties are about to change. + public virtual void OnChanged(int dispid) + { + if (dispid == (int)VSLangProj80.VsProjPropId.VBPROJPROPID_FileName) + { + // Get the filename of the nested project. Inetead of asking the label on the nested we ask the filename, since the label might not yet been set. + IVsProject3 nestedProject = this.nestedHierarchy as IVsProject3; + + if (nestedProject != null) + { + string document; + ErrorHandler.ThrowOnFailure(nestedProject.GetMkDocument(VSConstants.VSITEMID_ROOT, out document)); + this.RenameNestedProjectInParentProject(Path.GetFileNameWithoutExtension(document)); + + // We need to redraw the caption since for some reason, by intervining to the OnChanged event the Caption is not updated. + this.ReDraw(UIHierarchyElement.Caption); + } + } + } + + /// + /// Notifies a sink that a [requestedit] property is about to change and that the object is asking the sink how to proceed. + /// + /// Dispatch identifier of the property that is about to change or DISPID_UNKNOWN if multiple properties are about to change. + public virtual void OnRequestEdit(int dispid) + { + + } + + #endregion + + #region public methods + #endregion + + #region overridden methods + + /// + /// Get the automation object for the NestedProjectNode + /// + /// An instance of the Automation.OANestedProjectItem type if succeded + public override object GetAutomationObject() + { + //Validate that we are not disposed or the project is closing + if (this.isDisposed || this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return null; + } + + return new Automation.OANestedProjectItem(this.ProjectMgr.GetAutomationObject() as Automation.OAProject, this); + } + + /// + /// Gets properties of a given node or of the hierarchy. + /// + /// Identifier of the hierarchy property + /// It return an object which type is dependent on the propid. + public override object GetProperty(int propId) + { + __VSHPROPID vshPropId = (__VSHPROPID)propId; + switch (vshPropId) + { + default: + return base.GetProperty(propId); + + case __VSHPROPID.VSHPROPID_Expandable: + return true; + + case __VSHPROPID.VSHPROPID_BrowseObject: + case __VSHPROPID.VSHPROPID_HandlesOwnReload: + return this.DelegateGetPropertyToNested(propId); + } + } + + + /// + /// Gets properties whose values are GUIDs. + /// + /// Identifier of the hierarchy property + /// Pointer to a GUID property specified in propid + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public override int GetGuidProperty(int propid, out Guid guid) + { + guid = Guid.Empty; + switch ((__VSHPROPID)propid) + { + case __VSHPROPID.VSHPROPID_ProjectIDGuid: + guid = this.projectInstanceGuid; + break; + + default: + return base.GetGuidProperty(propid, out guid); + } + + CCITracing.TraceCall(String.Format(CultureInfo.CurrentCulture, "Guid for {0} property", propid)); + if (guid.CompareTo(Guid.Empty) == 0) + { + return VSConstants.DISP_E_MEMBERNOTFOUND; + } + + return VSConstants.S_OK; + } + + + /// + /// Determines whether the hierarchy item changed. + /// + /// Item identifier of the hierarchy item contained in VSITEMID + /// Pointer to the IUnknown interface of the hierarchy item. + /// TRUE if the hierarchy item changed. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public override int IsItemDirty(uint itemId, IntPtr punkDocData, out int pfDirty) + { + Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method"); + Debug.Assert(punkDocData != IntPtr.Zero, "docData intptr was zero"); + + // Get an IPersistFileFormat object from docData object + IPersistFileFormat persistFileFormat = Marshal.GetTypedObjectForIUnknown(punkDocData, typeof(IPersistFileFormat)) as IPersistFileFormat; + Debug.Assert(persistFileFormat != null, "The docData object does not implement the IPersistFileFormat interface"); + + // Call IsDirty on the IPersistFileFormat interface + ErrorHandler.ThrowOnFailure(persistFileFormat.IsDirty(out pfDirty)); + + return VSConstants.S_OK; + } + + /// + /// Saves the hierarchy item to disk. + /// + /// Flags whose values are taken from the VSSAVEFLAGS enumeration. + /// File name to be applied when dwSave is set to VSSAVE_SilentSave. + /// Item identifier of the hierarchy item saved from VSITEMID. + /// Pointer to the IUnknown interface of the hierarchy item saved. + /// TRUE if the save action was canceled. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public override int SaveItem(VSSAVEFLAGS dwSave, string silentSaveAsName, uint itemid, IntPtr punkDocData, out int pfCancelled) + { + // Don't ignore/unignore file changes + // Use Advise/Unadvise to work around rename situations + try + { + this.StopObservingNestedProjectFile(); + Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method"); + Debug.Assert(punkDocData != IntPtr.Zero, "docData intptr was zero"); + + // Get an IPersistFileFormat object from docData object (we don't call release on punkDocData since did not increment its ref count) + IPersistFileFormat persistFileFormat = Marshal.GetTypedObjectForIUnknown(punkDocData, typeof(IPersistFileFormat)) as IPersistFileFormat; + Debug.Assert(persistFileFormat != null, "The docData object does not implement the IPersistFileFormat interface"); + + IVsUIShell uiShell = this.GetService(typeof(SVsUIShell)) as IVsUIShell; + string newName; + ErrorHandler.ThrowOnFailure(uiShell.SaveDocDataToFile(dwSave, persistFileFormat, silentSaveAsName, out newName, out pfCancelled)); + + // When supported do a rename of the nested project here + } + finally + { + // Succeeded or not we must hook to the file change events + // Don't ignore/unignore file changes + // Use Advise/Unadvise to work around rename situations + this.ObserveNestedProjectFile(); + } + + return VSConstants.S_OK; + } + + /// + /// Gets the icon handle. It tries first the nested to get the icon handle. If that is not supported it will get it from + /// the image list of the nested if that is supported. If neither of these is supported a default image will be shown. + /// + /// An object representing the icon. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "Microsoft.VisualStudio.Shell.Interop.IVsHierarchy.GetProperty(System.UInt32,System.Int32,System.Object@)")] + public override object GetIconHandle(bool open) + { + Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method"); + + object iconHandle = null; + this.nestedHierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_IconHandle, out iconHandle); + if (iconHandle == null) + { + if (null == imageHandler) + { + InitImageHandler(); + } + // Try to get an icon from the nested hierrachy image list. + if (imageHandler.ImageList != null) + { + object imageIndexAsObject = null; + if (this.nestedHierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_IconIndex, out imageIndexAsObject) == VSConstants.S_OK && + imageIndexAsObject != null) + { + int imageIndex = (int)imageIndexAsObject; + if (imageIndex < imageHandler.ImageList.Images.Count) + { + iconHandle = imageHandler.GetIconHandle(imageIndex); + } + } + } + + if (null == iconHandle) + { + iconHandle = this.ProjectMgr.ImageHandler.GetIconHandle((int)ProjectNode.ImageName.Application); + } + } + + return iconHandle; + } + + /// + /// Return S_OK. Implementation of Closing a nested project is done in CloseNestedProject which is called by CloseChildren. + /// + /// S_OK + public override int Close() + { + return VSConstants.S_OK; + } + + + /// + /// Returns the moniker of the nested project. + /// + /// + public override string GetMkDocument() + { + Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method"); + if (this.isDisposed || this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return String.Empty; + } + + return this.projectPath; + } + + /// + /// Called by the shell when a node has been renamed from the GUI + /// + /// The name of the new label. + /// A success or failure value. + public override int SetEditLabel(string label) + { + int result = this.DelegateSetPropertyToNested((int)__VSHPROPID.VSHPROPID_EditLabel, label); + if (ErrorHandler.Succeeded(result)) + { + this.RenameNestedProjectInParentProject(label); + } + + return result; + } + + /// + /// Called by the shell to get the node caption when the user tries to rename from the GUI + /// + /// the node cation + public override string GetEditLabel() + { + return (string)this.DelegateGetPropertyToNested((int)__VSHPROPID.VSHPROPID_EditLabel); + } + + /// + /// This is temporary until we have support for re-adding a nested item + /// + protected override bool CanDeleteItem(__VSDELETEITEMOPERATION deleteOperation) + { + return false; + } + + /// + /// Delegates the call to the inner hierarchy. + /// + /// Reserved parameter defined at the IVsPersistHierarchyItem2::ReloadItem parameter. + protected internal override void ReloadItem(uint reserved) + { + #region precondition + if (this.isDisposed || this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + throw new InvalidOperationException(); + } + + Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method"); + #endregion + + IVsPersistHierarchyItem2 persistHierachyItem = this.nestedHierarchy as IVsPersistHierarchyItem2; + + // We are expecting that if we get called then the nestedhierarchy supports IVsPersistHierarchyItem2, since then hierrachy should support handling its own reload. + // There should be no errormessage to the user since this is an internal error, that it cannot be fixed at user level. + if (persistHierachyItem == null) + { + throw new InvalidOperationException(); + } + + ErrorHandler.ThrowOnFailure(persistHierachyItem.ReloadItem(VSConstants.VSITEMID_ROOT, reserved)); + } + + /// + /// Flag indicating that changes to a file can be ignored when item is saved or reloaded. + /// + /// Flag indicating whether or not to ignore changes (1 to ignore, 0 to stop ignoring). + protected internal override void IgnoreItemFileChanges(bool ignoreFlag) + { + #region precondition + if (this.isDisposed || this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + throw new InvalidOperationException(); + } + + Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method"); + #endregion + + this.IgnoreNestedProjectFile(ignoreFlag); + + IVsPersistHierarchyItem2 persistHierachyItem = this.nestedHierarchy as IVsPersistHierarchyItem2; + + // If the IVsPersistHierarchyItem2 is not implemented by the nested just return + if (persistHierachyItem == null) + { + return; + } + + ErrorHandler.ThrowOnFailure(persistHierachyItem.IgnoreItemFileChanges(VSConstants.VSITEMID_ROOT, ignoreFlag ? 1 : 0)); + } + + /// + /// Sets the VSADDFILEFLAGS that will be used to call the IVsTrackProjectDocumentsEvents2 OnAddFiles + /// + /// The files to which an array of VSADDFILEFLAGS has to be specified. + /// + protected internal override VSADDFILEFLAGS[] GetAddFileFlags(string[] files) + { + if (files == null || files.Length == 0) + { + return new VSADDFILEFLAGS[1] { VSADDFILEFLAGS.VSADDFILEFLAGS_NoFlags }; + } + + VSADDFILEFLAGS[] addFileFlags = new VSADDFILEFLAGS[files.Length]; + + for (int i = 0; i < files.Length; i++) + { + addFileFlags[i] = VSADDFILEFLAGS.VSADDFILEFLAGS_IsNestedProjectFile; + } + + return addFileFlags; + } + + /// + /// Sets the VSQUERYADDFILEFLAGS that will be used to call the IVsTrackProjectDocumentsEvents2 OnQueryAddFiles + /// + /// The files to which an array of VSADDFILEFLAGS has to be specified. + /// + protected internal override VSQUERYADDFILEFLAGS[] GetQueryAddFileFlags(string[] files) + { + if (files == null || files.Length == 0) + { + return new VSQUERYADDFILEFLAGS[1] { VSQUERYADDFILEFLAGS.VSQUERYADDFILEFLAGS_NoFlags }; + } + + VSQUERYADDFILEFLAGS[] queryAddFileFlags = new VSQUERYADDFILEFLAGS[files.Length]; + + for (int i = 0; i < files.Length; i++) + { + queryAddFileFlags[i] = VSQUERYADDFILEFLAGS.VSQUERYADDFILEFLAGS_IsNestedProjectFile; + } + + return queryAddFileFlags; + } + + /// + /// Sets the VSREMOVEFILEFLAGS that will be used to call the IVsTrackProjectDocumentsEvents2 OnRemoveFiles + /// + /// The files to which an array of VSREMOVEFILEFLAGS has to be specified. + /// + protected internal override VSREMOVEFILEFLAGS[] GetRemoveFileFlags(string[] files) + { + if (files == null || files.Length == 0) + { + return new VSREMOVEFILEFLAGS[1] { VSREMOVEFILEFLAGS.VSREMOVEFILEFLAGS_NoFlags }; + } + + VSREMOVEFILEFLAGS[] removeFileFlags = new VSREMOVEFILEFLAGS[files.Length]; + + for (int i = 0; i < files.Length; i++) + { + removeFileFlags[i] = VSREMOVEFILEFLAGS.VSREMOVEFILEFLAGS_IsNestedProjectFile; + } + + return removeFileFlags; + } + + /// + /// Sets the VSQUERYREMOVEFILEFLAGS that will be used to call the IVsTrackProjectDocumentsEvents2 OnQueryRemoveFiles + /// + /// The files to which an array of VSQUERYREMOVEFILEFLAGS has to be specified. + /// + protected internal override VSQUERYREMOVEFILEFLAGS[] GetQueryRemoveFileFlags(string[] files) + { + if (files == null || files.Length == 0) + { + return new VSQUERYREMOVEFILEFLAGS[1] { VSQUERYREMOVEFILEFLAGS.VSQUERYREMOVEFILEFLAGS_NoFlags }; + } + + VSQUERYREMOVEFILEFLAGS[] queryRemoveFileFlags = new VSQUERYREMOVEFILEFLAGS[files.Length]; + + for (int i = 0; i < files.Length; i++) + { + queryRemoveFileFlags[i] = VSQUERYREMOVEFILEFLAGS.VSQUERYREMOVEFILEFLAGS_IsNestedProjectFile; + } + + return queryRemoveFileFlags; + } + #endregion + + #region virtual methods + /// + /// Initialize the nested hierarhy node. + /// + /// The file name of the nested project. + /// The location of the nested project. + /// The name of the project. + /// The nested project creation flags + /// This methos should be called just after a NestedProjectNode object is created. + public virtual void Init(string fileName, string destination, string projectName, __VSCREATEPROJFLAGS createFlags) + { + if (String.IsNullOrEmpty(fileName)) + { + throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "fileName"); + } + + if (String.IsNullOrEmpty(destination)) + { + throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "destination"); + } + + this.projectName = Path.GetFileName(fileName); + this.projectPath = Path.Combine(destination, this.projectName); + + // get the IVsSolution interface from the global service provider + IVsSolution solution = this.GetService(typeof(IVsSolution)) as IVsSolution; + Debug.Assert(solution != null, "Could not get the IVsSolution object from the services exposed by this project"); + if (solution == null) + { + throw new InvalidOperationException(); + } + + // Get the project type guid from project element + string typeGuidString = this.ItemNode.GetMetadataAndThrow(ProjectFileConstants.TypeGuid, new InvalidOperationException()); + Guid projectFactoryGuid = Guid.Empty; + if (!String.IsNullOrEmpty(typeGuidString)) + { + projectFactoryGuid = new Guid(typeGuidString); + } + + // Get the project factory. + IVsProjectFactory projectFactory; + ErrorHandler.ThrowOnFailure(solution.GetProjectFactory((uint)0, new Guid[] { projectFactoryGuid }, fileName, out projectFactory)); + + this.CreateProjectDirectory(); + + //Create new project using factory + int cancelled; + Guid refiid = NativeMethods.IID_IUnknown; + IntPtr projectPtr = IntPtr.Zero; + + try + { + ErrorHandler.ThrowOnFailure(projectFactory.CreateProject(fileName, destination, projectName, (uint)createFlags, ref refiid, out projectPtr, out cancelled)); + + if (projectPtr != IntPtr.Zero) + { + this.nestedHierarchy = Marshal.GetTypedObjectForIUnknown(projectPtr, typeof(IVsHierarchy)) as IVsHierarchy; + Debug.Assert(this.nestedHierarchy != null, "Nested hierarchy could not be created"); + Debug.Assert(cancelled == 0); + } + } + finally + { + if (projectPtr != IntPtr.Zero) + { + // We created a new instance of the project, we need to call release to decrement the ref count + // the RCW (this.nestedHierarchy) still has a reference to it which will keep it alive + Marshal.Release(projectPtr); + } + } + + if (cancelled != 0 && this.nestedHierarchy == null) + { + ErrorHandler.ThrowOnFailure(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + + // Link into the nested VS hierarchy. + ErrorHandler.ThrowOnFailure(this.nestedHierarchy.SetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ParentHierarchy, this.ProjectMgr)); + ErrorHandler.ThrowOnFailure(this.nestedHierarchy.SetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ParentHierarchyItemid, (object)(int)this.ID)); + + this.LockRDTEntry(); + + this.ConnectPropertyNotifySink(); + } + + /// + /// Links a nested project as a virtual project to the solution. + /// + protected internal virtual void AddVirtualProject() + { + // This is the second step in creating and adding a nested project. The inner hierarchy must have been + // already initialized at this point. + #region precondition + if (this.nestedHierarchy == null) + { + throw new InvalidOperationException(); + } + #endregion + // get the IVsSolution interface from the global service provider + IVsSolution solution = this.GetService(typeof(IVsSolution)) as IVsSolution; + Debug.Assert(solution != null, "Could not get the IVsSolution object from the services exposed by this project"); + if (solution == null) + { + throw new InvalidOperationException(); + } + + this.InitializeInstanceGuid(); + + // Add virtual project to solution. + ErrorHandler.ThrowOnFailure(solution.AddVirtualProjectEx(this.nestedHierarchy, this.VirtualProjectFlags, ref this.projectInstanceGuid)); + + // Now set up to listen on file changes on the nested project node. + this.ObserveNestedProjectFile(); + } + + /// + /// The method that does the cleanup. + /// + /// + protected override void Dispose(bool disposing) + { + // Everybody can go here. + if (!this.isDisposed) + { + try + { + // Synchronize calls to the Dispose simulteniously. + lock (Mutex) + { + if (disposing) + { + this.DisconnectPropertyNotifySink(); + this.StopObservingNestedProjectFile(); + + // If a project cannot load it may happen that the imagehandler is not instantiated. + if (this.imageHandler != null) + { + this.imageHandler.Close(); + } + } + } + } + finally + { + base.Dispose(disposing); + this.isDisposed = true; + } + } + } + + /// + /// Creates the project directory if it does not exist. + /// + /// + protected virtual void CreateProjectDirectory() + { + string directoryName = Path.GetDirectoryName(this.projectPath); + + if (!Directory.Exists(directoryName)) + { + Directory.CreateDirectory(directoryName); + } + } + + + /// + /// Lock the RDT Entry for the nested project. + /// By default this document is marked as "Dont Save as". That means the menu File->SaveAs is disabled for the + /// nested project node. + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "RDT")] + protected virtual void LockRDTEntry() + { + // Define flags for the nested project document + _VSRDTFLAGS flags = _VSRDTFLAGS.RDT_VirtualDocument | _VSRDTFLAGS.RDT_ProjSlnDocument; ; + + // Request the RDT service + IVsRunningDocumentTable rdt = this.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + Debug.Assert(rdt != null, " Could not get running document table from the services exposed by this project"); + if (rdt == null) + { + throw new InvalidOperationException(); + } + + // First we see if someone else has opened the requested view of the file. + uint itemid; + IntPtr docData = IntPtr.Zero; + IVsHierarchy ivsHierarchy; + uint docCookie; + IntPtr projectPtr = IntPtr.Zero; + + try + { + ErrorHandler.ThrowOnFailure(rdt.FindAndLockDocument((uint)flags, this.projectPath, out ivsHierarchy, out itemid, out docData, out docCookie)); + flags |= _VSRDTFLAGS.RDT_EditLock; + + if (ivsHierarchy != null && docCookie != (uint)ShellConstants.VSDOCCOOKIE_NIL) + { + if (docCookie != this.DocCookie) + { + this.DocCookie = docCookie; + } + } + else + { + + // get inptr for hierarchy + projectPtr = Marshal.GetIUnknownForObject(this.nestedHierarchy); + Debug.Assert(projectPtr != IntPtr.Zero, " Project pointer for the nested hierarchy has not been initialized"); + ErrorHandler.ThrowOnFailure(rdt.RegisterAndLockDocument((uint)flags, this.projectPath, this.ProjectMgr, this.ID, projectPtr, out docCookie)); + + this.DocCookie = docCookie; + Debug.Assert(this.DocCookie != (uint)ShellConstants.VSDOCCOOKIE_NIL, "Invalid cookie when registering document in the running document table."); + + //we must also set the doc cookie on the nested hier + this.SetDocCookieOnNestedHier(this.DocCookie); + } + } + finally + { + // Release all Inptr's that that were given as out pointers + if (docData != IntPtr.Zero) + { + Marshal.Release(docData); + } + if (projectPtr != IntPtr.Zero) + { + Marshal.Release(projectPtr); + } + } + + } + + /// + /// Unlock the RDT entry for the nested project + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "RDT")] + protected virtual void UnlockRDTEntry() + { + if (this.isDisposed || this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return; + } + // First we see if someone else has opened the requested view of the file. + IVsRunningDocumentTable rdt = this.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + if (rdt != null && this.DocCookie != (int)ShellConstants.VSDOCCOOKIE_NIL) + { + _VSRDTFLAGS flags = _VSRDTFLAGS.RDT_EditLock; + + ErrorHandler.ThrowOnFailure(rdt.UnlockDocument((uint)flags, (uint)this.DocCookie)); + } + + this.DocCookie = (int)ShellConstants.VSDOCCOOKIE_NIL; + } + + /// + /// Renames the project file in the parent project structure. + /// + /// The new label. + protected virtual void RenameNestedProjectInParentProject(string label) + { + string existingLabel = this.Caption; + + if (String.Compare(existingLabel, label, StringComparison.Ordinal) == 0) + { + return; + } + + string oldFileName = this.projectPath; + string oldPath = this.Url; + + try + { + this.StopObservingNestedProjectFile(); + this.ProjectMgr.SuspendMSBuild(); + + // Check out the project file if necessary. + if (!this.ProjectMgr.QueryEditProjectFile(false)) + { + throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + + + string newFileName = label + Path.GetExtension(oldFileName); + this.SaveNestedProjectItemInProjectFile(newFileName); + + string projectDirectory = Path.GetDirectoryName(oldFileName); + + // update state. + this.projectName = newFileName; + this.projectPath = Path.Combine(projectDirectory, this.projectName); + + // Unload and lock the RDT entries + this.UnlockRDTEntry(); + this.LockRDTEntry(); + + // Since actually this is a rename in our hierarchy notify the tracker that a rename has happened. + this.ProjectMgr.Tracker.OnItemRenamed(oldPath, this.projectPath, VSRENAMEFILEFLAGS.VSRENAMEFILEFLAGS_IsNestedProjectFile); + } + finally + { + this.ObserveNestedProjectFile(); + this.ProjectMgr.ResumeMSBuild(this.ProjectMgr.ReEvaluateProjectFileTargetName); + } + } + /// + /// Saves the nested project information in the project file. + /// + /// + protected virtual void SaveNestedProjectItemInProjectFile(string newFileName) + { + string existingInclude = this.ItemNode.Item.EvaluatedInclude; + string existingRelativePath = Path.GetDirectoryName(existingInclude); + string newRelativePath = Path.Combine(existingRelativePath, newFileName); + this.ItemNode.Rename(newRelativePath); + } + #endregion + + #region helper methods + /// + /// Closes a nested project and releases the nested hierrachy pointer. + /// + internal void CloseNestedProjectNode() + { + if (this.isDisposed || this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return; + } + + uint itemid = VSConstants.VSITEMID_NIL; + try + { + this.DisconnectPropertyNotifySink(); + + IVsUIHierarchy hier; + + IVsWindowFrame windowFrame; + VsShellUtilities.IsDocumentOpen(this.ProjectMgr.Site, this.projectPath, Guid.Empty, out hier, out itemid, out windowFrame); + + + if (itemid == VSConstants.VSITEMID_NIL) + { + this.UnlockRDTEntry(); + } + + IVsSolution solution = this.GetService(typeof(IVsSolution)) as IVsSolution; + if (solution == null) + { + throw new InvalidOperationException(); + } + + ErrorHandler.ThrowOnFailure(solution.RemoveVirtualProject(this.nestedHierarchy, 0)); + + } + finally + { + this.StopObservingNestedProjectFile(); + + // if we haven't already release the RDT cookie, do so now. + if (itemid == VSConstants.VSITEMID_NIL) + { + this.UnlockRDTEntry(); + } + + this.Dispose(true); + } + } + + private void InitializeInstanceGuid() + { + if (this.projectInstanceGuid != Guid.Empty) + { + return; + } + + Guid instanceGuid = Guid.Empty; + + Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method"); + + // This method should be called from the open children method, then we can safely use the IsNewProject property + if (this.ProjectMgr.IsNewProject) + { + instanceGuid = Guid.NewGuid(); + this.ItemNode.SetMetadata(ProjectFileConstants.InstanceGuid, instanceGuid.ToString("B")); + ErrorHandler.ThrowOnFailure(this.nestedHierarchy.SetGuidProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ProjectIDGuid, ref instanceGuid)); + } + else + { + // Get a guid from the nested hiererachy. + Guid nestedHiererachyInstanceGuid; + ErrorHandler.ThrowOnFailure(this.nestedHierarchy.GetGuidProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ProjectIDGuid, out nestedHiererachyInstanceGuid)); + + // Get instance guid from the project file. If it does not exist then we create one. + string instanceGuidAsString = this.ItemNode.GetMetadata(ProjectFileConstants.InstanceGuid); + + // 1. nestedHiererachyInstanceGuid is empty and instanceGuidAsString is empty then create a new one. + // 2. nestedHiererachyInstanceGuid is empty and instanceGuidAsString not empty use instanceGuidAsString and update the nested project object by calling SetGuidProperty. + // 3. nestedHiererachyInstanceGuid is not empty instanceGuidAsString is empty then use nestedHiererachyInstanceGuid and update the outer project element. + // 4. nestedHiererachyInstanceGuid is not empty instanceGuidAsString is empty then use nestedHiererachyInstanceGuid and update the outer project element. + + if (nestedHiererachyInstanceGuid == Guid.Empty && String.IsNullOrEmpty(instanceGuidAsString)) + { + instanceGuid = Guid.NewGuid(); + } + else if (nestedHiererachyInstanceGuid == Guid.Empty && !String.IsNullOrEmpty(instanceGuidAsString)) + { + instanceGuid = new Guid(instanceGuidAsString); + + ErrorHandler.ThrowOnFailure(this.nestedHierarchy.SetGuidProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ProjectIDGuid, ref instanceGuid)); + } + else if (nestedHiererachyInstanceGuid != Guid.Empty) + { + instanceGuid = nestedHiererachyInstanceGuid; + + // If the instanceGuidAsString is empty then creating a guid out of it would throw an exception. + if (String.IsNullOrEmpty(instanceGuidAsString) || nestedHiererachyInstanceGuid != new Guid(instanceGuidAsString)) + { + this.ItemNode.SetMetadata(ProjectFileConstants.InstanceGuid, instanceGuid.ToString("B")); + } + } + } + + this.projectInstanceGuid = instanceGuid; + } + + private void SetDocCookieOnNestedHier(uint itemDocCookie) + { + object docCookie = (int)itemDocCookie; + + try + { + ErrorHandler.ThrowOnFailure(this.nestedHierarchy.SetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ItemDocCookie, docCookie)); + } + catch (NotImplementedException) + { + //we swallow this exception on purpose + } + } + + private void InitImageHandler() + { + Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method"); + + if (null == imageHandler) + { + imageHandler = new ImageHandler(); + } + object imageListAsPointer = null; + ErrorHandler.ThrowOnFailure(this.nestedHierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_IconImgList, out imageListAsPointer)); + if (imageListAsPointer != null) + { + this.imageHandler.ImageList = Utilities.GetImageList(imageListAsPointer); + } + } + + /// + /// Delegates Getproperty calls to the inner nested. + /// + /// The property to delegate. + /// The return of the GetProperty from nested. + private object DelegateGetPropertyToNested(int propID) + { + if (!this.ProjectMgr.IsClosed) + { + Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method"); + + object returnValue; + + // Do not throw since some project types will return E_FAIL if they do not support a property. + int result = this.nestedHierarchy.GetProperty(VSConstants.VSITEMID_ROOT, propID, out returnValue); + if (ErrorHandler.Succeeded(result)) + { + return returnValue; + } + } + + return null; + } + + /// + /// Delegates Setproperty calls to the inner nested. + /// + /// The property to delegate. + /// The property to set. + /// The return of the SetProperty from nested. + private int DelegateSetPropertyToNested(int propID, object value) + { + if (this.ProjectMgr.IsClosed) + { + return VSConstants.E_FAIL; + } + + Debug.Assert(this.nestedHierarchy != null, "The nested hierarchy object must be created before calling this method"); + + // Do not throw since some project types will return E_FAIL if they do not support a property. + return this.nestedHierarchy.SetProperty(VSConstants.VSITEMID_ROOT, propID, value); + } + + /// + /// Starts observing changes on this file. + /// + private void ObserveNestedProjectFile() + { + ProjectContainerNode parent = this.ProjectMgr as ProjectContainerNode; + Debug.Assert(parent != null, "The parent project for nested projects should be subclassed from ProjectContainerNode"); + parent.NestedProjectNodeReloader.ObserveItem(this.GetMkDocument(), this.ID); + } + + /// + /// Stops observing changes on this file. + /// + private void StopObservingNestedProjectFile() + { + ProjectContainerNode parent = this.ProjectMgr as ProjectContainerNode; + Debug.Assert(parent != null, "The parent project for nested projects should be subclassed from ProjectContainerNode"); + parent.NestedProjectNodeReloader.StopObservingItem(this.GetMkDocument()); + } + + /// + /// Ignores observing changes on this file depending on the boolean flag. + /// + /// Flag indicating whether or not to ignore changes (1 to ignore, 0 to stop ignoring). + private void IgnoreNestedProjectFile(bool ignoreFlag) + { + ProjectContainerNode parent = this.ProjectMgr as ProjectContainerNode; + Debug.Assert(parent != null, "The parent project for nested projects should be subclassed from ProjectContainerNode"); + parent.NestedProjectNodeReloader.IgnoreItemChanges(this.GetMkDocument(), ignoreFlag); + } + + /// + /// We need to advise property notify sink on project properties so that + /// we know when the project file is renamed through a property. + /// + private void ConnectPropertyNotifySink() + { + if (this.projectPropertyNotifySinkCookie != (uint)ShellConstants.VSCOOKIE_NIL) + { + return; + } + + IConnectionPoint connectionPoint = this.GetConnectionPointFromPropertySink(); + if (connectionPoint != null) + { + connectionPoint.Advise(this, out this.projectPropertyNotifySinkCookie); + } + } + + /// + /// Disconnects the propertynotify sink + /// + private void DisconnectPropertyNotifySink() + { + if (this.projectPropertyNotifySinkCookie == (uint)ShellConstants.VSCOOKIE_NIL) + { + return; + } + + IConnectionPoint connectionPoint = this.GetConnectionPointFromPropertySink(); + if (connectionPoint != null) + { + connectionPoint.Unadvise(this.projectPropertyNotifySinkCookie); + this.projectPropertyNotifySinkCookie = (uint)ShellConstants.VSCOOKIE_NIL; + } + } + + /// + /// Gets a ConnectionPoint for the IPropertyNotifySink interface. + /// + /// + private IConnectionPoint GetConnectionPointFromPropertySink() + { + IConnectionPoint connectionPoint = null; + object browseObject = this.GetProperty((int)__VSHPROPID.VSHPROPID_BrowseObject); + IConnectionPointContainer connectionPointContainer = browseObject as IConnectionPointContainer; + + if (connectionPointContainer != null) + { + Guid guid = typeof(IPropertyNotifySink).GUID; + connectionPointContainer.FindConnectionPoint(ref guid, out connectionPoint); + } + + return connectionPoint; + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/NodeProperties.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/NodeProperties.cs new file mode 100644 index 0000000000..7790506a98 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/NodeProperties.cs @@ -0,0 +1,806 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; + +namespace Microsoft.VisualStudio.Project +{ + + /// + /// All public properties on Nodeproperties or derived classes are assumed to be used by Automation by default. + /// Set this attribute to false on Properties that should not be visible for Automation. + /// + [AttributeUsage(AttributeTargets.Property, AllowMultiple = false, Inherited = true)] + public sealed class AutomationBrowsableAttribute : System.Attribute + { + public AutomationBrowsableAttribute(bool browsable) + { + this.browsable = browsable; + } + + public bool Browsable + { + get + { + return this.browsable; + } + } + + private bool browsable; + } + + /// + /// To create your own localizable node properties, subclass this and add public properties + /// decorated with your own localized display name, category and description attributes. + /// + [CLSCompliant(false), ComVisible(true)] + public class NodeProperties : LocalizableProperties, + ISpecifyPropertyPages, + IVsGetCfgProvider, + IVsSpecifyProjectDesignerPages, + EnvDTE80.IInternalExtenderProvider, + IVsBrowseObject + { + #region fields + private HierarchyNode node; + #endregion + + #region properties + [Browsable(false)] + [AutomationBrowsable(false)] + public HierarchyNode Node + { + get { return this.node; } + } + + /// + /// Used by Property Pages Frame to set it's title bar. The Caption of the Hierarchy Node is returned. + /// + [Browsable(false)] + [AutomationBrowsable(false)] + public virtual string Name + { + get { return this.node.Caption; } + } + + #endregion + + #region ctors + public NodeProperties(HierarchyNode node) + { + if(node == null) + { + throw new ArgumentNullException("node"); + } + this.node = node; + } + #endregion + + #region ISpecifyPropertyPages methods + public virtual void GetPages(CAUUID[] pages) + { + this.GetCommonPropertyPages(pages); + } + #endregion + + #region IVsSpecifyProjectDesignerPages + /// + /// Implementation of the IVsSpecifyProjectDesignerPages. It will retun the pages that are configuration independent. + /// + /// The pages to return. + /// + public virtual int GetProjectDesignerPages(CAUUID[] pages) + { + this.GetCommonPropertyPages(pages); + return VSConstants.S_OK; + } + #endregion + + #region IVsGetCfgProvider methods + public virtual int GetCfgProvider(out IVsCfgProvider p) + { + p = null; + return VSConstants.E_NOTIMPL; + } + #endregion + + #region IVsBrowseObject methods + /// + /// Maps back to the hierarchy or project item object corresponding to the browse object. + /// + /// Reference to the hierarchy object. + /// Reference to the project item. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int GetProjectItem(out IVsHierarchy hier, out uint itemid) + { + if(this.node == null) + { + throw new InvalidOperationException(); + } + hier = HierarchyNode.GetOuterHierarchy(this.node.ProjectMgr); + itemid = this.node.ID; + return VSConstants.S_OK; + } + #endregion + + #region overridden methods + /// + /// Get the Caption of the Hierarchy Node instance. If Caption is null or empty we delegate to base + /// + /// Caption of Hierarchy node instance + public override string GetComponentName() + { + string caption = this.Node.Caption; + if(string.IsNullOrEmpty(caption)) + { + return base.GetComponentName(); + } + else + { + return caption; + } + } + #endregion + + #region helper methods + protected string GetProperty(string name, string def) + { + string a = this.Node.ItemNode.GetMetadata(name); + return (a == null) ? def : a; + } + + protected void SetProperty(string name, string value) + { + this.Node.ItemNode.SetMetadata(name, value); + } + + /// + /// Retrieves the common property pages. The NodeProperties is the BrowseObject and that will be called to support + /// configuration independent properties. + /// + /// The pages to return. + private void GetCommonPropertyPages(CAUUID[] pages) + { + // We do not check whether the supportsProjectDesigner is set to false on the ProjectNode. + // We rely that the caller knows what to call on us. + if(pages == null) + { + throw new ArgumentNullException("pages"); + } + + if(pages.Length == 0) + { + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "pages"); + } + + // Only the project should show the property page the rest should show the project properties. + if(this.node != null && (this.node is ProjectNode)) + { + // Retrieve the list of guids from hierarchy properties. + // Because a flavor could modify that list we must make sure we are calling the outer most implementation of IVsHierarchy + string guidsList = String.Empty; + IVsHierarchy hierarchy = HierarchyNode.GetOuterHierarchy(this.Node.ProjectMgr); + object variant = null; + ErrorHandler.ThrowOnFailure(hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID2.VSHPROPID_PropertyPagesCLSIDList, out variant)); + guidsList = (string)variant; + + Guid[] guids = Utilities.GuidsArrayFromSemicolonDelimitedStringOfGuids(guidsList); + if(guids == null || guids.Length == 0) + { + pages[0] = new CAUUID(); + pages[0].cElems = 0; + } + else + { + pages[0] = PackageUtilities.CreateCAUUIDFromGuidArray(guids); + } + } + else + { + pages[0] = new CAUUID(); + pages[0].cElems = 0; + } + } + #endregion + + #region IInternalExtenderProvider Members + + bool EnvDTE80.IInternalExtenderProvider.CanExtend(string extenderCATID, string extenderName, object extendeeObject) + { + EnvDTE80.IInternalExtenderProvider outerHierarchy = HierarchyNode.GetOuterHierarchy(this.Node) as EnvDTE80.IInternalExtenderProvider; + + + if(outerHierarchy != null) + { + return outerHierarchy.CanExtend(extenderCATID, extenderName, extendeeObject); + } + return false; + } + + object EnvDTE80.IInternalExtenderProvider.GetExtender(string extenderCATID, string extenderName, object extendeeObject, EnvDTE.IExtenderSite extenderSite, int cookie) + { + EnvDTE80.IInternalExtenderProvider outerHierarchy = HierarchyNode.GetOuterHierarchy(this.Node) as EnvDTE80.IInternalExtenderProvider; + + if(outerHierarchy != null) + { + return outerHierarchy.GetExtender(extenderCATID, extenderName, extendeeObject, extenderSite, cookie); + } + + return null; + } + + object EnvDTE80.IInternalExtenderProvider.GetExtenderNames(string extenderCATID, object extendeeObject) + { + EnvDTE80.IInternalExtenderProvider outerHierarchy = HierarchyNode.GetOuterHierarchy(this.Node) as EnvDTE80.IInternalExtenderProvider; + + if(outerHierarchy != null) + { + return outerHierarchy.GetExtenderNames(extenderCATID, extendeeObject); + } + + return null; + } + + #endregion + + #region ExtenderSupport + [Browsable(false)] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CATID")] + public virtual string ExtenderCATID + { + get + { + Guid catid = this.Node.ProjectMgr.GetCATIDForType(this.GetType()); + if(Guid.Empty.CompareTo(catid) == 0) + { + return null; + } + return catid.ToString("B"); + } + } + + [Browsable(false)] + public object ExtenderNames() + { + EnvDTE.ObjectExtenders extenderService = (EnvDTE.ObjectExtenders)this.Node.GetService(typeof(EnvDTE.ObjectExtenders)); + Debug.Assert(extenderService != null, "Could not get the ObjectExtenders object from the services exposed by this property object"); + if(extenderService == null) + { + throw new InvalidOperationException(); + } + return extenderService.GetExtenderNames(this.ExtenderCATID, this); + } + + public object Extender(string extenderName) + { + EnvDTE.ObjectExtenders extenderService = (EnvDTE.ObjectExtenders)this.Node.GetService(typeof(EnvDTE.ObjectExtenders)); + Debug.Assert(extenderService != null, "Could not get the ObjectExtenders object from the services exposed by this property object"); + if(extenderService == null) + { + throw new InvalidOperationException(); + } + return extenderService.GetExtender(this.ExtenderCATID, extenderName, this); + } + + #endregion + } + + [CLSCompliant(false), ComVisible(true)] + public class FileNodeProperties : NodeProperties + { + #region properties + [SRCategoryAttribute(SR.Advanced)] + [LocDisplayName(SR.BuildAction)] + [SRDescriptionAttribute(SR.BuildActionDescription)] + public virtual BuildAction BuildAction + { + get + { + string value = this.Node.ItemNode.ItemName; + if(value == null || value.Length == 0) + { + return BuildAction.None; + } + return (BuildAction)Enum.Parse(typeof(BuildAction), value); + } + set + { + this.Node.ItemNode.ItemName = value.ToString(); + } + } + + [SRCategoryAttribute(SR.Misc)] + [LocDisplayName(SR.FileName)] + [SRDescriptionAttribute(SR.FileNameDescription)] + public string FileName + { + get + { + return this.Node.Caption; + } + set + { + this.Node.SetEditLabel(value); + } + } + + [SRCategoryAttribute(SR.Misc)] + [LocDisplayName(SR.FullPath)] + [SRDescriptionAttribute(SR.FullPathDescription)] + public string FullPath + { + get + { + return this.Node.Url; + } + } + + #region non-browsable properties - used for automation only + [Browsable(false)] + public string Extension + { + get + { + return Path.GetExtension(this.Node.Caption); + } + } + #endregion + + #endregion + + #region ctors + public FileNodeProperties(HierarchyNode node) + : base(node) + { + } + #endregion + + #region overridden methods + public override string GetClassName() + { + return SR.GetString(SR.FileProperties, CultureInfo.CurrentUICulture); + } + #endregion + } + + [CLSCompliant(false), ComVisible(true)] + public class DependentFileNodeProperties : NodeProperties + { + #region properties + [SRCategoryAttribute(SR.Advanced)] + [LocDisplayName(SR.BuildAction)] + [SRDescriptionAttribute(SR.BuildActionDescription)] + public virtual BuildAction BuildAction + { + get + { + string value = this.Node.ItemNode.ItemName; + if(value == null || value.Length == 0) + { + return BuildAction.None; + } + return (BuildAction)Enum.Parse(typeof(BuildAction), value); + } + set + { + this.Node.ItemNode.ItemName = value.ToString(); + } + } + + [SRCategoryAttribute(SR.Misc)] + [LocDisplayName(SR.FileName)] + [SRDescriptionAttribute(SR.FileNameDescription)] + public virtual string FileName + { + get + { + return this.Node.Caption; + } + } + + [SRCategoryAttribute(SR.Misc)] + [LocDisplayName(SR.FullPath)] + [SRDescriptionAttribute(SR.FullPathDescription)] + public string FullPath + { + get + { + return this.Node.Url; + } + } + #endregion + + #region ctors + public DependentFileNodeProperties(HierarchyNode node) + : base(node) + { + } + + #endregion + + #region overridden methods + public override string GetClassName() + { + return SR.GetString(SR.FileProperties, CultureInfo.CurrentUICulture); + } + #endregion + } + + [CLSCompliant(false), ComVisible(true)] + public class SingleFileGeneratorNodeProperties : FileNodeProperties + { + #region fields + private EventHandler onCustomToolChanged; + private EventHandler onCustomToolNameSpaceChanged; + private string _customTool = ""; + private string _customToolNamespace = ""; + #endregion + + #region custom tool events + internal event EventHandler OnCustomToolChanged + { + add { onCustomToolChanged += value; } + remove { onCustomToolChanged -= value; } + } + + internal event EventHandler OnCustomToolNameSpaceChanged + { + add { onCustomToolNameSpaceChanged += value; } + remove { onCustomToolNameSpaceChanged -= value; } + } + + #endregion + + #region properties + [SRCategoryAttribute(SR.Advanced)] + [LocDisplayName(SR.CustomTool)] + [SRDescriptionAttribute(SR.CustomToolDescription)] + public virtual string CustomTool + { + get + { + _customTool = this.Node.ItemNode.GetMetadata(ProjectFileConstants.Generator); + return _customTool; + } + set + { + _customTool = value; + if(!string.IsNullOrEmpty(_customTool)) + { + this.Node.ItemNode.SetMetadata(ProjectFileConstants.Generator, _customTool); + HierarchyNodeEventArgs args = new HierarchyNodeEventArgs(this.Node); + if(onCustomToolChanged != null) + { + onCustomToolChanged(this.Node, args); + } + } + } + } + + [SRCategoryAttribute(VisualStudio.Project.SR.Advanced)] + [LocDisplayName(SR.CustomToolNamespace)] + [SRDescriptionAttribute(SR.CustomToolNamespaceDescription)] + public virtual string CustomToolNamespace + { + get + { + _customToolNamespace = this.Node.ItemNode.GetMetadata(ProjectFileConstants.CustomToolNamespace); + return _customToolNamespace; + } + set + { + _customToolNamespace = value; + if(!string.IsNullOrEmpty(_customToolNamespace)) + { + this.Node.ItemNode.SetMetadata(ProjectFileConstants.CustomToolNamespace, _customToolNamespace); + HierarchyNodeEventArgs args = new HierarchyNodeEventArgs(this.Node); + if(onCustomToolNameSpaceChanged != null) + { + onCustomToolNameSpaceChanged(this.Node, args); + } + } + } + } + #endregion + + #region ctors + public SingleFileGeneratorNodeProperties(HierarchyNode node) + : base(node) + { + } + #endregion + } + + [CLSCompliant(false), ComVisible(true)] + public class ProjectNodeProperties : NodeProperties + { + #region properties + [SRCategoryAttribute(SR.Misc)] + [LocDisplayName(SR.ProjectFolder)] + [SRDescriptionAttribute(SR.ProjectFolderDescription)] + [AutomationBrowsable(false)] + public string ProjectFolder + { + get + { + return this.Node.ProjectMgr.ProjectFolder; + } + } + + [SRCategoryAttribute(SR.Misc)] + [LocDisplayName(SR.ProjectFile)] + [SRDescriptionAttribute(SR.ProjectFileDescription)] + [AutomationBrowsable(false)] + public string ProjectFile + { + get + { + return this.Node.ProjectMgr.ProjectFile; + } + set + { + this.Node.ProjectMgr.ProjectFile = value; + } + } + + #region non-browsable properties - used for automation only + [Browsable(false)] + public string FileName + { + get + { + return this.Node.ProjectMgr.ProjectFile; + } + set + { + this.Node.ProjectMgr.ProjectFile = value; + } + } + + + [Browsable(false)] + public string FullPath + { + get + { + string fullPath = this.Node.ProjectMgr.ProjectFolder; + if(!fullPath.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) + { + return fullPath + Path.DirectorySeparatorChar; + } + else + { + return fullPath; + } + } + } + #endregion + + #endregion + + #region ctors + public ProjectNodeProperties(ProjectNode node) + : base(node) + { + } + #endregion + + #region overridden methods + public override string GetClassName() + { + return SR.GetString(SR.ProjectProperties, CultureInfo.CurrentUICulture); + } + + /// + /// ICustomTypeDescriptor.GetEditor + /// To enable the "Property Pages" button on the properties browser + /// the browse object (project properties) need to be unmanaged + /// or it needs to provide an editor of type ComponentEditor. + /// + /// Type of the editor + /// Editor + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", + Justification="The service provider is used by the PropertiesEditorLauncher")] + public override object GetEditor(Type editorBaseType) + { + // Override the scenario where we are asked for a ComponentEditor + // as this is how the Properties Browser calls us + if(editorBaseType == typeof(ComponentEditor)) + { + IOleServiceProvider sp; + ErrorHandler.ThrowOnFailure(this.Node.GetSite(out sp)); + return new PropertiesEditorLauncher(new ServiceProvider(sp)); + } + + return base.GetEditor(editorBaseType); + } + + public override int GetCfgProvider(out IVsCfgProvider p) + { + if(this.Node != null && this.Node.ProjectMgr != null) + { + return this.Node.ProjectMgr.GetCfgProvider(out p); + } + + return base.GetCfgProvider(out p); + } + #endregion + } + + [CLSCompliant(false), ComVisible(true)] + public class FolderNodeProperties : NodeProperties + { + #region properties + [SRCategoryAttribute(SR.Misc)] + [LocDisplayName(SR.FolderName)] + [SRDescriptionAttribute(SR.FolderNameDescription)] + [AutomationBrowsable(false)] + public string FolderName + { + get + { + return this.Node.Caption; + } + set + { + this.Node.SetEditLabel(value); + this.Node.ReDraw(UIHierarchyElement.Caption); + } + } + + #region properties - used for automation only + [Browsable(false)] + [AutomationBrowsable(true)] + public string FileName + { + get + { + return this.Node.Caption; + } + set + { + this.Node.SetEditLabel(value); + } + } + + [Browsable(false)] + [AutomationBrowsable(true)] + public string FullPath + { + get + { + string fullPath = this.Node.GetMkDocument(); + if(!fullPath.EndsWith(Path.DirectorySeparatorChar.ToString(), StringComparison.Ordinal)) + { + return fullPath + Path.DirectorySeparatorChar; + } + else + { + return fullPath; + } + } + } + #endregion + + #endregion + + #region ctors + public FolderNodeProperties(HierarchyNode node) + : base(node) + { + } + #endregion + + #region overridden methods + public override string GetClassName() + { + return SR.GetString(SR.FolderProperties, CultureInfo.CurrentUICulture); + } + #endregion + } + + [CLSCompliant(false), ComVisible(true)] + public class ReferenceNodeProperties : NodeProperties + { + #region properties + [SRCategoryAttribute(SR.Misc)] + [LocDisplayName(SR.RefName)] + [SRDescriptionAttribute(SR.RefNameDescription)] + [Browsable(true)] + [AutomationBrowsable(true)] + public override string Name + { + get + { + return this.Node.Caption; + } + } + + [SRCategoryAttribute(SR.Misc)] + [LocDisplayName(SR.CopyToLocal)] + [SRDescriptionAttribute(SR.CopyToLocalDescription)] + public bool CopyToLocal + { + get + { + string copyLocal = this.GetProperty(ProjectFileConstants.Private, "False"); + if(copyLocal == null || copyLocal.Length == 0) + return true; + return bool.Parse(copyLocal); + } + set + { + this.SetProperty(ProjectFileConstants.Private, value.ToString()); + } + } + + [SRCategoryAttribute(SR.Misc)] + [LocDisplayName(SR.FullPath)] + [SRDescriptionAttribute(SR.FullPathDescription)] + public virtual string FullPath + { + get + { + return this.Node.Url; + } + } + #endregion + + #region ctors + public ReferenceNodeProperties(HierarchyNode node) + : base(node) + { + } + #endregion + + #region overridden methods + public override string GetClassName() + { + return SR.GetString(SR.ReferenceProperties, CultureInfo.CurrentUICulture); + } + #endregion + } + + [ComVisible(true)] + public class ProjectReferencesProperties : ReferenceNodeProperties + { + #region ctors + public ProjectReferencesProperties(ProjectReferenceNode node) + : base(node) + { + } + #endregion + + #region overriden methods + public override string FullPath + { + get + { + return ((ProjectReferenceNode)Node).ReferencedProjectOutputPath; + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/OleServiceProvider.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/OleServiceProvider.cs new file mode 100644 index 0000000000..33477880f2 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/OleServiceProvider.cs @@ -0,0 +1,263 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; + +namespace Microsoft.VisualStudio.Project +{ + public class OleServiceProvider : IOleServiceProvider, IDisposable + { + #region Public Types + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1034:NestedTypesShouldNotBeVisible")] + public delegate object ServiceCreatorCallback(Type serviceType); + #endregion + + #region Private Types + private class ServiceData : IDisposable + { + private Type serviceType; + private object instance; + private ServiceCreatorCallback creator; + private bool shouldDispose; + public ServiceData(Type serviceType, object instance, ServiceCreatorCallback callback, bool shouldDispose) + { + if(null == serviceType) + { + throw new ArgumentNullException("serviceType"); + } + + if((null == instance) && (null == callback)) + { + throw new ArgumentNullException("instance"); + } + + this.serviceType = serviceType; + this.instance = instance; + this.creator = callback; + this.shouldDispose = shouldDispose; + } + + public object ServiceInstance + { + get + { + if(null == instance) + { + instance = creator(serviceType); + } + return instance; + } + } + + public Guid Guid + { + get { return serviceType.GUID; } + } + + public void Dispose() + { + if((shouldDispose) && (null != instance)) + { + IDisposable disp = instance as IDisposable; + if(null != disp) + { + disp.Dispose(); + } + instance = null; + } + creator = null; + GC.SuppressFinalize(this); + } + } + #endregion + + #region fields + + private Dictionary services = new Dictionary(); + private bool isDisposed; + /// + /// Defines an object that will be a mutex for this object for synchronizing thread calls. + /// + private static volatile object Mutex = new object(); + #endregion + + #region ctors + public OleServiceProvider() + { + } + #endregion + + #region IOleServiceProvider Members + + public int QueryService(ref Guid guidService, ref Guid riid, out IntPtr ppvObject) + { + ppvObject = (IntPtr)0; + int hr = VSConstants.S_OK; + + ServiceData serviceInstance = null; + + if(services != null && services.ContainsKey(guidService)) + { + serviceInstance = services[guidService]; + } + + if(serviceInstance == null) + { + return VSConstants.E_NOINTERFACE; + } + + // Now check to see if the user asked for an IID other than + // IUnknown. If so, we must do another QI. + // + if(riid.Equals(NativeMethods.IID_IUnknown)) + { + ppvObject = Marshal.GetIUnknownForObject(serviceInstance.ServiceInstance); + } + else + { + IntPtr pUnk = IntPtr.Zero; + try + { + pUnk = Marshal.GetIUnknownForObject(serviceInstance.ServiceInstance); + hr = Marshal.QueryInterface(pUnk, ref riid, out ppvObject); + } + finally + { + if(pUnk != IntPtr.Zero) + { + Marshal.Release(pUnk); + } + } + } + + return hr; + } + + #endregion + + #region Dispose + + /// + /// The IDispose interface Dispose method for disposing the object determinastically. + /// + public void Dispose() + { + Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + + /// + /// Adds the given service to the service container. + /// + /// The type of the service to add. + /// An instance of the service. + /// true if the Dipose of the service provider is allowed to dispose the sevice instance. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", + Justification = "The services created here will be disposed in the Dispose method of this type.")] + public void AddService(Type serviceType, object serviceInstance, bool shouldDisposeServiceInstance) + { + // Create the description of this service. Note that we don't do any validation + // of the parameter here because the constructor of ServiceData will do it for us. + ServiceData service = new ServiceData(serviceType, serviceInstance, null, shouldDisposeServiceInstance); + + // Now add the service desctription to the dictionary. + AddService(service); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", + Justification="The services created here will be disposed in the Dispose method of this type.")] + public void AddService(Type serviceType, ServiceCreatorCallback callback, bool shouldDisposeServiceInstance) + { + // Create the description of this service. Note that we don't do any validation + // of the parameter here because the constructor of ServiceData will do it for us. + ServiceData service = new ServiceData(serviceType, null, callback, shouldDisposeServiceInstance); + + // Now add the service desctription to the dictionary. + AddService(service); + } + + private void AddService(ServiceData data) + { + // Make sure that the collection of services is created. + if(null == services) + { + services = new Dictionary(); + } + + // Disallow the addition of duplicate services. + if(services.ContainsKey(data.Guid)) + { + throw new InvalidOperationException(); + } + + services.Add(data.Guid, data); + } + + /// + /// Removes the given service type from the service container. + /// + public void RemoveService(Type serviceType) + { + if(serviceType == null) + { + throw new ArgumentNullException("serviceType"); + } + + if(services.ContainsKey(serviceType.GUID)) + { + services.Remove(serviceType.GUID); + } + } + + #region helper methods + /// + /// The method that does the cleanup. + /// + /// + protected virtual void Dispose(bool disposing) + { + // Everybody can go here. + if(!this.isDisposed) + { + // Synchronize calls to the Dispose simulteniously. + lock(Mutex) + { + if(disposing) + { + // Remove all our services + if(services != null) + { + foreach(ServiceData data in services.Values) + { + data.Dispose(); + } + services.Clear(); + services = null; + } + } + + this.isDisposed = true; + } + } + } + #endregion + + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Output.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Output.cs new file mode 100644 index 0000000000..be94b235b4 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Output.cs @@ -0,0 +1,121 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.Build.Execution; + +namespace Microsoft.VisualStudio.Project +{ + class Output : IVsOutput2 + { + private ProjectNode project; + private ProjectItemInstance output; + + /// + /// Constructor for IVSOutput2 implementation + /// + /// Project that produce this output + /// MSBuild generated item corresponding to the output assembly (by default, these would be of type MainAssembly + public Output(ProjectNode projectManager, ProjectItemInstance outputAssembly) + { + if(projectManager == null) + throw new ArgumentNullException("projectManager"); + if(outputAssembly == null) + throw new ArgumentNullException("outputAssembly"); + + project = projectManager; + output = outputAssembly; + } + + #region IVsOutput2 Members + + public int get_CanonicalName(out string pbstrCanonicalName) + { + // Get the output assembly path (including the name) + pbstrCanonicalName = output.GetMetadataValue(ProjectFileConstants.Include); + Debug.Assert(!String.IsNullOrEmpty(pbstrCanonicalName), "Output Assembly not defined"); + + // Make sure we have a full path + if(!System.IO.Path.IsPathRooted(pbstrCanonicalName)) + { + pbstrCanonicalName = new Url(project.BaseURI, pbstrCanonicalName).AbsoluteUrl; + } + return VSConstants.S_OK; + } + + /// + /// This path must start with file:/// if it wants other project + /// to be able to reference the output on disk. + /// If the output is not on disk, then this requirement does not + /// apply as other projects probably don't know how to access it. + /// + public virtual int get_DeploySourceURL(out string pbstrDeploySourceURL) + { + string path = output.GetMetadataValue(ProjectFileConstants.FinalOutputPath); + if(string.IsNullOrEmpty(path)) + { + throw new InvalidOperationException(); + } + if(path.Length < 9 || String.Compare(path.Substring(0, 8), "file:///", StringComparison.OrdinalIgnoreCase) != 0) + path = "file:///" + path; + pbstrDeploySourceURL = path; + return VSConstants.S_OK; + } + + public int get_DisplayName(out string pbstrDisplayName) + { + return this.get_CanonicalName(out pbstrDisplayName); + } + + public virtual int get_Property(string szProperty, out object pvar) + { + String value = output.GetMetadataValue(szProperty); + pvar = value; + + // If we don't have a value, we are expected to return unimplemented + return String.IsNullOrEmpty(value) ? VSConstants.E_NOTIMPL : VSConstants.S_OK; + } + + public int get_RootRelativeURL(out string pbstrRelativePath) + { + pbstrRelativePath = String.Empty; + object variant; + // get the corresponding property + + if(ErrorHandler.Succeeded(this.get_Property("TargetPath", out variant))) + { + string var = variant as String; + + if(var != null) + { + pbstrRelativePath = var; + } + } + + return VSConstants.S_OK; + } + + public virtual int get_Type(out Guid pguidType) + { + pguidType = Guid.Empty; + throw new NotImplementedException(); + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/OutputGroup.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/OutputGroup.cs new file mode 100644 index 0000000000..4b3bbeb9e3 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/OutputGroup.cs @@ -0,0 +1,268 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using MSBuild = Microsoft.Build.Evaluation; +using MSBuildExecution = Microsoft.Build.Execution; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Allows projects to group outputs according to usage. + /// + [CLSCompliant(false), ComVisible(true)] + public class OutputGroup : IVsOutputGroup2 + { + #region fields + private ProjectConfig projectCfg; + private ProjectNode project; + + private List outputs = new List(); + private Output keyOutput; + private string name; + private string targetName; + #endregion + + #region properties + /// + /// Get the project configuration object associated with this output group + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Cfg")] + protected ProjectConfig ProjectCfg + { + get { return projectCfg; } + } + + /// + /// Get the project object that produces this output group. + /// + protected ProjectNode Project + { + get { return project; } + } + + /// + /// Gets the msbuild target name which is assciated to the outputgroup. + /// ProjectNode defines a static collection of output group names and their associated MsBuild target + /// + protected string TargetName + { + get { return targetName; } + } + #endregion + + #region ctors + + /// + /// Constructor for IVSOutputGroup2 implementation + /// + /// Name of the output group. See VS_OUTPUTGROUP_CNAME_Build in vsshell.idl for the list of standard values + /// MSBuild target name + /// Project that produce this output + /// Configuration that produce this output + public OutputGroup(string outputName, string msBuildTargetName, ProjectNode projectManager, ProjectConfig configuration) + { + if(outputName == null) + throw new ArgumentNullException("outputName"); + if(msBuildTargetName == null) + throw new ArgumentNullException("outputName"); + if(projectManager == null) + throw new ArgumentNullException("projectManager"); + if(configuration == null) + throw new ArgumentNullException("configuration"); + + name = outputName; + targetName = msBuildTargetName; + project = projectManager; + projectCfg = configuration; + } + #endregion + + #region virtual methods + protected virtual void Refresh() + { + // Let MSBuild know which configuration we are working with + project.SetConfiguration(projectCfg.ConfigName); + + // Generate dependencies if such a task exist + const string generateDependencyList = "AllProjectOutputGroups"; + if(project.BuildProject.Targets.ContainsKey(generateDependencyList)) + { + bool succeeded = false; + project.BuildTarget(generateDependencyList, out succeeded); + Debug.Assert(succeeded, "Failed to build target: " + generateDependencyList); + } + + // Rebuild the content of our list of output + string outputType = this.targetName + "Output"; + this.outputs.Clear(); + foreach (MSBuildExecution.ProjectItemInstance assembly in project.CurrentConfig.GetItems(outputType)) + { + Output output = new Output(project, assembly); + this.outputs.Add(output); + + // See if it is our key output + if(String.Compare(assembly.GetMetadataValue("IsKeyOutput"), true.ToString(), StringComparison.OrdinalIgnoreCase) == 0) + keyOutput = output; + } + + project.SetCurrentConfiguration(); + + // Now that the group is built we have to check if it is invalidated by a property + // change on the project. + project.OnProjectPropertyChanged += new EventHandler(OnProjectPropertyChanged); + } + + public virtual void InvalidateGroup() + { + // Set keyOutput to null so that a refresh will be performed the next time + // a property getter is called. + if(null != keyOutput) + { + // Once the group is invalidated there is no more reason to listen for events. + project.OnProjectPropertyChanged -= new EventHandler(OnProjectPropertyChanged); + } + keyOutput = null; + } + #endregion + + #region event handlers + private void OnProjectPropertyChanged(object sender, ProjectPropertyChangedArgs args) + { + // In theory here we should decide if we have to invalidate the group according with the kind of property + // that is changed. + InvalidateGroup(); + } + #endregion + + #region IVsOutputGroup2 Members + + public virtual int get_CanonicalName(out string pbstrCanonicalName) + { + pbstrCanonicalName = this.name; + return VSConstants.S_OK; + } + + public virtual int get_DeployDependencies(uint celt, IVsDeployDependency[] rgpdpd, uint[] pcActual) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int get_Description(out string pbstrDescription) + { + pbstrDescription = null; + + string description; + int hr = this.get_CanonicalName(out description); + if(ErrorHandler.Succeeded(hr)) + pbstrDescription = this.Project.GetOutputGroupDescription(description); + return hr; + } + + public virtual int get_DisplayName(out string pbstrDisplayName) + { + pbstrDisplayName = null; + + string displayName; + int hr = this.get_CanonicalName(out displayName); + if(ErrorHandler.Succeeded(hr)) + pbstrDisplayName = this.Project.GetOutputGroupDisplayName(displayName); + return hr; + } + + public virtual int get_KeyOutput(out string pbstrCanonicalName) + { + pbstrCanonicalName = null; + if(keyOutput == null) + Refresh(); + if(keyOutput == null) + { + pbstrCanonicalName = String.Empty; + return VSConstants.S_FALSE; + } + return keyOutput.get_CanonicalName(out pbstrCanonicalName); + } + + public virtual int get_KeyOutputObject(out IVsOutput2 ppKeyOutput) + { + if(keyOutput == null) + Refresh(); + ppKeyOutput = keyOutput; + if(ppKeyOutput == null) + return VSConstants.S_FALSE; + return VSConstants.S_OK; + } + + public virtual int get_Outputs(uint celt, IVsOutput2[] rgpcfg, uint[] pcActual) + { + // Ensure that we are refreshed. This is somewhat of a hack that enables project to + // project reference scenarios to work. Normally, output groups are populated as part + // of build. However, in the project to project reference case, what ends up happening + // is that the referencing projects requests the referenced project's output group + // before a build is done on the referenced project. + // + // Furthermore, the project auto toolbox manager requires output groups to be populated + // on project reopen as well... + // + // In the end, this is probably the right thing to do, though -- as it keeps the output + // groups always up to date. + Refresh(); + + // See if only the caller only wants to know the count + if(celt == 0 || rgpcfg == null) + { + if(pcActual != null && pcActual.Length > 0) + pcActual[0] = (uint)outputs.Count; + return VSConstants.S_OK; + } + + // Fill the array with our outputs + uint count = 0; + foreach(Output output in outputs) + { + if(rgpcfg.Length > count && celt > count && output != null) + { + rgpcfg[count] = output; + ++count; + } + } + + if(pcActual != null && pcActual.Length > 0) + pcActual[0] = count; + + // If the number asked for does not match the number returned, return S_FALSE + return (count == celt) ? VSConstants.S_OK : VSConstants.S_FALSE; + } + + public virtual int get_ProjectCfg(out IVsProjectCfg2 ppIVsProjectCfg2) + { + ppIVsProjectCfg2 = (IVsProjectCfg2)this.projectCfg; + return VSConstants.S_OK; + } + + public virtual int get_Property(string pszProperty, out object pvar) + { + pvar = project.GetProjectProperty(pszProperty); + return VSConstants.S_OK; + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectBase.files b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectBase.files new file mode 100644 index 0000000000..ab5995121e --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectBase.files @@ -0,0 +1,470 @@ + + + + + v4.0 + $(AllowedAssemblyPrefix);Microsoft.Internal + + + + EnvDTE80.dll + EnvDTE80 + False + global + + + global + + + global + + + global + + + + + + + + global + + + + True + + + + + + + + . + + + + + ProjectBase\Diagrams\AutomationClasses.cd + true + + + ProjectBase\Diagrams\ConfigurationClasses.cd + true + + + ProjectBase\Diagrams\DocumentManagerClasses.cd + true + + + ProjectBase\Diagrams\HierarchyClasses.cd + true + + + ProjectBase\Diagrams\PropertiesClasses.cd + true + + + ProjectBase\Diagrams\ReferenceClasses.cd + true + + + + + + ProjectBase\misc\ConnectionPointContainer.cs + true + + + ProjectBase\Misc\ExternDll.cs + true + + + ProjectBase\Misc\NativeMethods.cs + true + + + ProjectBase\Misc\UnsafeNativeMethods.cs + true + + + + ProjectBase\AssemblyReferenceNode.cs + true + + + ProjectBase\Attributes.cs + true + + + + + ProjectBase\AutomationScope.cs + true + + + ProjectBase\OAFileItem.cs + true + + + ProjectBase\OAFolderItem.cs + true + + + ProjectBase\OANestedProjectItem.cs + true + + + ProjectBase\OANullProperty.cs + true + + + ProjectBase\OAProject.cs + true + + + ProjectBase\OAProjectItem.cs + true + + + ProjectBase\OAProjectItems.cs + true + + + ProjectBase\OAProperties.cs + true + + + ProjectBase\OAProperty.cs + true + + + ProjectBase\OANavigableProjectItems.cs + true + + + ProjectBase\OAReferenceFolderItem.cs + true + + + ProjectBase\OARreferenceItem.cs + true + + + ProjectBase\OASolutionFolder.cs + true + + + + + ProjectBase\LangProj\OAAssemblyReference.cs + true + + + ProjectBase\LangProj\OABuildManager.cs + true + + + ProjectBase\LangProj\OAComReference.cs + true + + + ProjectBase\LangProj\OAProjectReference.cs + true + + + ProjectBase\LangProj\OAReferenceBase.cs + true + + + ProjectBase\LangProj\OAReferences.cs + true + + + ProjectBase\LangProj\OAVSProject.cs + true + + + ProjectBase\LangProj\OAVSProjectItem.cs + true + + + + ProjectBase\BuildDependency.cs + true + + + ProjectBase\BuildPropertyPage.cs + true + + + ProjectBase\ComReferenceNode.cs + true + + + ProjectBase\ConfigProvider.cs + true + Code + + + ProjectBase\ConfigurationProperties.cs + true + Code + + + ProjectBase\DataObject.cs + true + Code + + + ProjectBase\DesignPropertyDescriptor.cs + true + + + ProjectBase\DocumentManager.cs + true + + + ProjectBase\EnumDependencies.cs + true + + + ProjectBase\FileChangeManager.cs + true + + + ProjectBase\FileDocumentManager.cs + true + + + ProjectBase\FileNode.cs + true + + + ProjectBase\DependentFileNode.cs + true + + + ProjectBase\FolderNode.cs + true + + + ProjectBase\GlobalSuppressions.cs + true + + + ProjectBase\HierarchyNode.cs + true + + + ProjectBase\ImageHandler.cs + true + + + + ProjectBase\IAggregatedHierarchy.cs + true + + + ProjectBase\IDEBuildLogger.cs + true + + + ProjectBase\LocalizableProperties.cs + true + + + ProjectBase\Project.cs + true + + + ProjectBase\NestedProjectBuildDependency.cs + true + + + ProjectBase\NestedProjectNode.cs + true + + + ProjectBase\NodeProperties.cs + true + + + ProjectBase\OleServiceProvider.cs + true + + + ProjectBase\Output.cs + true + + + ProjectBase\OutputGroup.cs + true + + + ProjectBase\ProjectConfig.cs + true + + + ProjectBase\ProjectContainerNode.cs + true + + + ProjectBase\ProjectDesignerDocumentManager.cs + true + + + ProjectBase\ProjectElement.cs + true + + + ProjectBase\ProjectFactory.cs + true + + + ProjectBase\ProjectFileConstants.cs + true + + + ProjectBase\ProjectNode.cs + true + + + ProjectBase\ProjectNode.CopyPaste.cs + true + + + ProjectBase\ProjectNode.Events.cs + true + + + ProjectBase\ProjectOptions.cs + true + + + ProjectBase\ProjectPackage.cs + true + + + ProjectBase\ProjectReferenceNode.cs + true + + + ProjectBase\PropertiesEditorLauncher.cs + true + + + ProjectBase\ReferenceContainerNode.cs + true + + + ProjectBase\ReferenceNode.cs + true + + + ProjectBase\RegisteredProjectType.cs + true + + + ProjectBase\SettingsPage.cs + true + + + ProjectBase\SingleFileGenerator.cs + true + + + ProjectBase\SingleFileGeneratorFactory.cs + true + + + ProjectBase\SolutionListener.cs + true + + + ProjectBase\SolutionListenerForBuildDependencyUpdate.cs + true + + + ProjectBase\SolutionListenerForProjectEvents.cs + true + + + ProjectBase\SolutionListenerForProjectOpen.cs + true + + + ProjectBase\SolutionListenerForProjectReferenceUpdate.cs + true + + + ProjectBase\StructuresEnums.cs + true + + + ProjectBase\SuspendFileChanges.cs + true + + + ProjectBase\Tracing.cs + true + + + ProjectBase\TokenProcessor.cs + true + + + ProjectBase\TrackDocumentsHelper.cs + true + + + ProjectBase\TypeConverters.cs + true + + + ProjectBase\UpdateSolutionEventsListener.cs + true + + + ProjectBase\Url.cs + true + + + ProjectBase\Utilities.cs + true + + + ProjectBase\VsCommands.cs + true + + + ProjectBase\VSShellUtilities.cs + true + + + ProjectBase\SelectionListener.cs + true + + + ProjectBase\ProjectDocumentsListener.cs + true + + + + + + VisualStudio.Package.Project.resx + true + Microsoft.VisualStudio.Project.resources + + + + + Resources\imagelis.bmp + true + Microsoft.VisualStudio.Project.Resources.imagelis.bmp + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectConfig.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectConfig.cs new file mode 100644 index 0000000000..f4606ab6eb --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectConfig.cs @@ -0,0 +1,1023 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +//#define ConfigTrace +using Microsoft.VisualStudio.Shell.Interop; +using MSBuild = Microsoft.Build.Evaluation; +using MSBuildExecution = Microsoft.Build.Execution; +using MSBuildConstruction = Microsoft.Build.Construction; + +namespace Microsoft.VisualStudio.Project +{ + [CLSCompliant(false), ComVisible(true)] + public class ProjectConfig : + IVsCfg, + IVsProjectCfg, + IVsProjectCfg2, + IVsProjectFlavorCfg, + IVsDebuggableProjectCfg, + ISpecifyPropertyPages, + IVsSpecifyProjectDesignerPages, + IVsCfgBrowseObject + { + #region constants + internal const string Debug = "Debug"; + internal const string Release = "Release"; + internal const string AnyCPU = "AnyCPU"; + #endregion + + #region fields + private ProjectNode project; + private string configName; + private MSBuildExecution.ProjectInstance currentConfig; + private List outputGroups; + private IProjectConfigProperties configurationProperties; + private IVsProjectFlavorCfg flavoredCfg; + private BuildableProjectConfig buildableCfg; + #endregion + + #region properties + public ProjectNode ProjectMgr + { + get + { + return this.project; + } + } + + public string ConfigName + { + get + { + return this.configName; + } + set + { + this.configName = value; + } + } + + public virtual object ConfigurationProperties + { + get + { + if(this.configurationProperties == null) + { + this.configurationProperties = new ProjectConfigProperties(this); + } + return this.configurationProperties; + } + } + + protected IList OutputGroups + { + get + { + if(null == this.outputGroups) + { + // Initialize output groups + this.outputGroups = new List(); + + // Get the list of group names from the project. + // The main reason we get it from the project is to make it easier for someone to modify + // it by simply overriding that method and providing the correct MSBuild target(s). + IList> groupNames = project.GetOutputGroupNames(); + + if(groupNames != null) + { + // Populate the output array + foreach(KeyValuePair group in groupNames) + { + OutputGroup outputGroup = CreateOutputGroup(project, group); + this.outputGroups.Add(outputGroup); + } + } + + } + return this.outputGroups; + } + } + #endregion + + #region ctors + public ProjectConfig(ProjectNode project, string configuration) + { + this.project = project; + this.configName = configuration; + + // Because the project can be aggregated by a flavor, we need to make sure + // we get the outer most implementation of that interface (hence: project --> IUnknown --> Interface) + IntPtr projectUnknown = Marshal.GetIUnknownForObject(this.ProjectMgr); + try + { + IVsProjectFlavorCfgProvider flavorCfgProvider = (IVsProjectFlavorCfgProvider)Marshal.GetTypedObjectForIUnknown(projectUnknown, typeof(IVsProjectFlavorCfgProvider)); + ErrorHandler.ThrowOnFailure(flavorCfgProvider.CreateProjectFlavorCfg(this, out flavoredCfg)); + if(flavoredCfg == null) + throw new COMException(); + } + finally + { + if(projectUnknown != IntPtr.Zero) + Marshal.Release(projectUnknown); + } + // if the flavored object support XML fragment, initialize it + IPersistXMLFragment persistXML = flavoredCfg as IPersistXMLFragment; + if(null != persistXML) + { + this.project.LoadXmlFragment(persistXML, this.DisplayName); + } + } + #endregion + + #region methods + protected virtual OutputGroup CreateOutputGroup(ProjectNode project, KeyValuePair group) + { + OutputGroup outputGroup = new OutputGroup(group.Key, group.Value, project, this); + return outputGroup; + } + + public void PrepareBuild(bool clean) + { + project.PrepareBuild(this.configName, clean); + } + + public virtual string GetConfigurationProperty(string propertyName, bool resetCache) + { + MSBuildExecution.ProjectPropertyInstance property = GetMsBuildProperty(propertyName, resetCache); + if (property == null) + return null; + + return property.EvaluatedValue; + } + + public virtual void SetConfigurationProperty(string propertyName, string propertyValue) + { + if(!this.project.QueryEditProjectFile(false)) + { + throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + + string condition = String.Format(CultureInfo.InvariantCulture, ConfigProvider.configString, this.ConfigName); + + SetPropertyUnderCondition(propertyName, propertyValue, condition); + + // property cache will need to be updated + this.currentConfig = null; + + // Signal the output groups that something is changed + foreach(OutputGroup group in this.OutputGroups) + { + group.InvalidateGroup(); + } + this.project.SetProjectFileDirty(true); + + return; + } + + /// + /// Emulates the behavior of SetProperty(name, value, condition) on the old MSBuild object model. + /// This finds a property group with the specified condition (or creates one if necessary) then sets the property in there. + /// + private void SetPropertyUnderCondition(string propertyName, string propertyValue, string condition) + { + string conditionTrimmed = (condition == null) ? String.Empty : condition.Trim(); + + if (conditionTrimmed.Length == 0) + { + this.project.BuildProject.SetProperty(propertyName, propertyValue); + return; + } + + // New OM doesn't have a convenient equivalent for setting a property with a particular property group condition. + // So do it ourselves. + MSBuildConstruction.ProjectPropertyGroupElement newGroup = null; + + foreach (MSBuildConstruction.ProjectPropertyGroupElement group in this.project.BuildProject.Xml.PropertyGroups) + { + if (String.Equals(group.Condition.Trim(), conditionTrimmed, StringComparison.OrdinalIgnoreCase)) + { + newGroup = group; + break; + } + } + + if (newGroup == null) + { + newGroup = this.project.BuildProject.Xml.AddPropertyGroup(); // Adds after last existing PG, else at start of project + newGroup.Condition = condition; + } + + foreach (MSBuildConstruction.ProjectPropertyElement property in newGroup.PropertiesReversed) // If there's dupes, pick the last one so we win + { + if (String.Equals(property.Name, propertyName, StringComparison.OrdinalIgnoreCase) && property.Condition.Length == 0) + { + property.Value = propertyValue; + return; + } + } + + newGroup.AddProperty(propertyName, propertyValue); + } + + /// + /// If flavored, and if the flavor config can be dirty, ask it if it is dirty + /// + /// Project file or user file + /// 0 = not dirty + internal int IsFlavorDirty(_PersistStorageType storageType) + { + int isDirty = 0; + if(this.flavoredCfg != null && this.flavoredCfg is IPersistXMLFragment) + { + ErrorHandler.ThrowOnFailure(((IPersistXMLFragment)this.flavoredCfg).IsFragmentDirty((uint)storageType, out isDirty)); + } + return isDirty; + } + + /// + /// If flavored, ask the flavor if it wants to provide an XML fragment + /// + /// Guid of the flavor + /// Project file or user file + /// Fragment that the flavor wants to save + /// HRESULT + internal int GetXmlFragment(Guid flavor, _PersistStorageType storageType, out string fragment) + { + fragment = null; + int hr = VSConstants.S_OK; + if(this.flavoredCfg != null && this.flavoredCfg is IPersistXMLFragment) + { + Guid flavorGuid = flavor; + hr = ((IPersistXMLFragment)this.flavoredCfg).Save(ref flavorGuid, (uint)storageType, out fragment, 1); + } + return hr; + } + #endregion + + #region IVsSpecifyPropertyPages + public void GetPages(CAUUID[] pages) + { + this.GetCfgPropertyPages(pages); + } + #endregion + + #region IVsSpecifyProjectDesignerPages + /// + /// Implementation of the IVsSpecifyProjectDesignerPages. It will retun the pages that are configuration dependent. + /// + /// The pages to return. + /// VSConstants.S_OK + public virtual int GetProjectDesignerPages(CAUUID[] pages) + { + this.GetCfgPropertyPages(pages); + return VSConstants.S_OK; + } + #endregion + + #region IVsCfg methods + /// + /// The display name is a two part item + /// first part is the config name, 2nd part is the platform name + /// + public virtual int get_DisplayName(out string name) + { + name = DisplayName; + return VSConstants.S_OK; + } + + private string DisplayName + { + get + { + string name; + string[] platform = new string[1]; + uint[] actual = new uint[1]; + name = this.configName; + // currently, we only support one platform, so just add it.. + IVsCfgProvider provider; + ErrorHandler.ThrowOnFailure(project.GetCfgProvider(out provider)); + ErrorHandler.ThrowOnFailure(((IVsCfgProvider2)provider).GetPlatformNames(1, platform, actual)); + if(!string.IsNullOrEmpty(platform[0])) + { + name += "|" + platform[0]; + } + return name; + } + } + public virtual int get_IsDebugOnly(out int fDebug) + { + fDebug = 0; + if(this.configName == "Debug") + { + fDebug = 1; + } + return VSConstants.S_OK; + } + public virtual int get_IsReleaseOnly(out int fRelease) + { + CCITracing.TraceCall(); + fRelease = 0; + if(this.configName == "Release") + { + fRelease = 1; + } + return VSConstants.S_OK; + } + #endregion + + #region IVsProjectCfg methods + public virtual int EnumOutputs(out IVsEnumOutputs eo) + { + CCITracing.TraceCall(); + eo = null; + return VSConstants.E_NOTIMPL; + } + + public virtual int get_BuildableProjectCfg(out IVsBuildableProjectCfg pb) + { + CCITracing.TraceCall(); + if(buildableCfg == null) + buildableCfg = new BuildableProjectConfig(this); + pb = buildableCfg; + return VSConstants.S_OK; + } + + public virtual int get_CanonicalName(out string name) + { + return ((IVsCfg)this).get_DisplayName(out name); + } + + public virtual int get_IsPackaged(out int pkgd) + { + CCITracing.TraceCall(); + pkgd = 0; + return VSConstants.S_OK; + } + + public virtual int get_IsSpecifyingOutputSupported(out int f) + { + CCITracing.TraceCall(); + f = 1; + return VSConstants.S_OK; + } + + public virtual int get_Platform(out Guid platform) + { + CCITracing.TraceCall(); + platform = Guid.Empty; + return VSConstants.E_NOTIMPL; + } + + public virtual int get_ProjectCfgProvider(out IVsProjectCfgProvider p) + { + CCITracing.TraceCall(); + p = null; + IVsCfgProvider cfgProvider = null; + this.project.GetCfgProvider(out cfgProvider); + if(cfgProvider != null) + { + p = cfgProvider as IVsProjectCfgProvider; + } + + return (null == p) ? VSConstants.E_NOTIMPL : VSConstants.S_OK; + } + + public virtual int get_RootURL(out string root) + { + CCITracing.TraceCall(); + root = null; + return VSConstants.S_OK; + } + + public virtual int get_TargetCodePage(out uint target) + { + CCITracing.TraceCall(); + target = (uint)System.Text.Encoding.Default.CodePage; + return VSConstants.S_OK; + } + + public virtual int get_UpdateSequenceNumber(ULARGE_INTEGER[] li) + { + if (li == null) + { + throw new ArgumentNullException("li"); + } + + CCITracing.TraceCall(); + li[0] = new ULARGE_INTEGER(); + li[0].QuadPart = 0; + return VSConstants.S_OK; + } + + public virtual int OpenOutput(string name, out IVsOutput output) + { + CCITracing.TraceCall(); + output = null; + return VSConstants.E_NOTIMPL; + } + #endregion + + #region IVsDebuggableProjectCfg methods + /// + /// Called by the vs shell to start debugging (managed or unmanaged). + /// Override this method to support other debug engines. + /// + /// A flag that determines the conditions under which to start the debugger. For valid grfLaunch values, see __VSDBGLAUNCHFLAGS + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code + public virtual int DebugLaunch(uint grfLaunch) + { + CCITracing.TraceCall(); + + try + { + VsDebugTargetInfo info = new VsDebugTargetInfo(); + info.cbSize = (uint)Marshal.SizeOf(info); + info.dlo = Microsoft.VisualStudio.Shell.Interop.DEBUG_LAUNCH_OPERATION.DLO_CreateProcess; + + // On first call, reset the cache, following calls will use the cached values + string property = GetConfigurationProperty("StartProgram", true); + if(string.IsNullOrEmpty(property)) + { + info.bstrExe = this.project.GetOutputAssembly(this.ConfigName); + } + else + { + info.bstrExe = property; + } + + property = GetConfigurationProperty("WorkingDirectory", false); + if(string.IsNullOrEmpty(property)) + { + info.bstrCurDir = Path.GetDirectoryName(info.bstrExe); + } + else + { + info.bstrCurDir = property; + } + + property = GetConfigurationProperty("CmdArgs", false); + if(!string.IsNullOrEmpty(property)) + { + info.bstrArg = property; + } + + property = GetConfigurationProperty("RemoteDebugMachine", false); + if(property != null && property.Length > 0) + { + info.bstrRemoteMachine = property; + } + + info.fSendStdoutToOutputWindow = 0; + + property = GetConfigurationProperty("EnableUnmanagedDebugging", false); + if(property != null && string.Compare(property, "true", StringComparison.OrdinalIgnoreCase) == 0) + { + //Set the unmanged debugger + //TODO change to vsconstant when it is available in VsConstants (guidNativeOnlyEng was the old name, maybe it has got a new name) + info.clsidCustom = new Guid("{3B476D35-A401-11D2-AAD4-00C04F990171}"); + } + else + { + //Set the managed debugger + info.clsidCustom = VSConstants.CLSID_ComPlusOnlyDebugEngine; + } + info.grfLaunch = grfLaunch; + VsShellUtilities.LaunchDebugger(this.project.Site, info); + } + catch(Exception e) + { + Trace.WriteLine("Exception : " + e.Message); + + return Marshal.GetHRForException(e); + } + + return VSConstants.S_OK; + } + + /// + /// Determines whether the debugger can be launched, given the state of the launch flags. + /// + /// Flags that determine the conditions under which to launch the debugger. + /// For valid grfLaunch values, see __VSDBGLAUNCHFLAGS or __VSDBGLAUNCHFLAGS2. + /// true if the debugger can be launched, otherwise false + /// S_OK if the method succeeds, otherwise an error code + public virtual int QueryDebugLaunch(uint flags, out int fCanLaunch) + { + CCITracing.TraceCall(); + string assembly = this.project.GetAssemblyName(this.ConfigName); + fCanLaunch = (assembly != null && assembly.ToUpperInvariant().EndsWith(".exe", StringComparison.OrdinalIgnoreCase)) ? 1 : 0; + if(fCanLaunch == 0) + { + string property = GetConfigurationProperty("StartProgram", true); + fCanLaunch = (property != null && property.Length > 0) ? 1 : 0; + } + return VSConstants.S_OK; + } + #endregion + + #region IVsProjectCfg2 Members + + public virtual int OpenOutputGroup(string szCanonicalName, out IVsOutputGroup ppIVsOutputGroup) + { + ppIVsOutputGroup = null; + // Search through our list of groups to find the one they are looking forgroupName + foreach(OutputGroup group in OutputGroups) + { + string groupName; + group.get_CanonicalName(out groupName); + if(String.Compare(groupName, szCanonicalName, StringComparison.OrdinalIgnoreCase) == 0) + { + ppIVsOutputGroup = group; + break; + } + } + return (ppIVsOutputGroup != null) ? VSConstants.S_OK : VSConstants.E_FAIL; + } + + public virtual int OutputsRequireAppRoot(out int pfRequiresAppRoot) + { + pfRequiresAppRoot = 0; + return VSConstants.E_NOTIMPL; + } + + public virtual int get_CfgType(ref Guid iidCfg, out IntPtr ppCfg) + { + // Delegate to the flavored configuration (to enable a flavor to take control) + // Since we can be asked for Configuration we don't support, avoid throwing and return the HRESULT directly + int hr = flavoredCfg.get_CfgType(ref iidCfg, out ppCfg); + + return hr; + } + + public virtual int get_IsPrivate(out int pfPrivate) + { + pfPrivate = 0; + return VSConstants.S_OK; + } + + public virtual int get_OutputGroups(uint celt, IVsOutputGroup[] rgpcfg, uint[] pcActual) + { + // Are they only asking for the number of groups? + if(celt == 0) + { + if((null == pcActual) || (0 == pcActual.Length)) + { + throw new ArgumentNullException("pcActual"); + } + pcActual[0] = (uint)OutputGroups.Count; + return VSConstants.S_OK; + } + + // Check that the array of output groups is not null + if((null == rgpcfg) || (rgpcfg.Length == 0)) + { + throw new ArgumentNullException("rgpcfg"); + } + + // Fill the array with our output groups + uint count = 0; + foreach(OutputGroup group in OutputGroups) + { + if(rgpcfg.Length > count && celt > count && group != null) + { + rgpcfg[count] = group; + ++count; + } + } + + if(pcActual != null && pcActual.Length > 0) + pcActual[0] = count; + + // If the number asked for does not match the number returned, return S_FALSE + return (count == celt) ? VSConstants.S_OK : VSConstants.S_FALSE; + } + + public virtual int get_VirtualRoot(out string pbstrVRoot) + { + pbstrVRoot = null; + return VSConstants.E_NOTIMPL; + } + + #endregion + + #region IVsCfgBrowseObject + /// + /// Maps back to the configuration corresponding to the browse object. + /// + /// The IVsCfg object represented by the browse object + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int GetCfg(out IVsCfg cfg) + { + cfg = this; + return VSConstants.S_OK; + } + + /// + /// Maps back to the hierarchy or project item object corresponding to the browse object. + /// + /// Reference to the hierarchy object. + /// Reference to the project item. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int GetProjectItem(out IVsHierarchy hier, out uint itemid) + { + if(this.project == null || this.project.NodeProperties == null) + { + throw new InvalidOperationException(); + } + return this.project.NodeProperties.GetProjectItem(out hier, out itemid); + } + #endregion + + #region helper methods + /// + /// Splits the canonical configuration name into platform and configuration name. + /// + /// The canonicalName name. + /// The name of the configuration. + /// The name of the platform. + /// true if successfull. + internal static bool TrySplitConfigurationCanonicalName(string canonicalName, out string configName, out string platformName) + { + configName = String.Empty; + platformName = String.Empty; + + if(String.IsNullOrEmpty(canonicalName)) + { + return false; + } + + string[] splittedCanonicalName = canonicalName.Split(new char[] { '|' }, StringSplitOptions.RemoveEmptyEntries); + + if(splittedCanonicalName == null || (splittedCanonicalName.Length != 1 && splittedCanonicalName.Length != 2)) + { + return false; + } + + configName = splittedCanonicalName[0]; + if(splittedCanonicalName.Length == 2) + { + platformName = splittedCanonicalName[1]; + } + + return true; + } + + private MSBuildExecution.ProjectPropertyInstance GetMsBuildProperty(string propertyName, bool resetCache) + { + if (resetCache || this.currentConfig == null) + { + // Get properties for current configuration from project file and cache it + this.project.SetConfiguration(this.ConfigName); + this.project.BuildProject.ReevaluateIfNecessary(); + // Create a snapshot of the evaluated project in its current state + this.currentConfig = this.project.BuildProject.CreateProjectInstance(); + + // Restore configuration + project.SetCurrentConfiguration(); + } + + if (this.currentConfig == null) + throw new Exception("Failed to retrieve properties"); + + // return property asked for + return this.currentConfig.GetProperty(propertyName); + } + + /// + /// Retrieves the configuration dependent property pages. + /// + /// The pages to return. + private void GetCfgPropertyPages(CAUUID[] pages) + { + // We do not check whether the supportsProjectDesigner is set to true on the ProjectNode. + // We rely that the caller knows what to call on us. + if(pages == null) + { + throw new ArgumentNullException("pages"); + } + + if(pages.Length == 0) + { + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "pages"); + } + + // Retrive the list of guids from hierarchy properties. + // Because a flavor could modify that list we must make sure we are calling the outer most implementation of IVsHierarchy + string guidsList = String.Empty; + IVsHierarchy hierarchy = HierarchyNode.GetOuterHierarchy(this.project); + object variant = null; + ErrorHandler.ThrowOnFailure(hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID2.VSHPROPID_CfgPropertyPagesCLSIDList, out variant), new int[] { VSConstants.DISP_E_MEMBERNOTFOUND, VSConstants.E_NOTIMPL }); + guidsList = (string)variant; + + Guid[] guids = Utilities.GuidsArrayFromSemicolonDelimitedStringOfGuids(guidsList); + if(guids == null || guids.Length == 0) + { + pages[0] = new CAUUID(); + pages[0].cElems = 0; + } + else + { + pages[0] = PackageUtilities.CreateCAUUIDFromGuidArray(guids); + } + } + #endregion + + #region IVsProjectFlavorCfg Members + /// + /// This is called to let the flavored config let go + /// of any reference it may still be holding to the base config + /// + /// + int IVsProjectFlavorCfg.Close() + { + // This is used to release the reference the flavored config is holding + // on the base config, but in our scenario these 2 are the same object + // so we have nothing to do here. + return VSConstants.S_OK; + } + + /// + /// Actual implementation of get_CfgType. + /// When not flavored or when the flavor delegate to use + /// we end up creating the requested config if we support it. + /// + /// IID representing the type of config object we should create + /// Config object that the method created + /// HRESULT + int IVsProjectFlavorCfg.get_CfgType(ref Guid iidCfg, out IntPtr ppCfg) + { + ppCfg = IntPtr.Zero; + + // See if this is an interface we support + if(iidCfg == typeof(IVsDebuggableProjectCfg).GUID) + ppCfg = Marshal.GetComInterfaceForObject(this, typeof(IVsDebuggableProjectCfg)); + else if(iidCfg == typeof(IVsBuildableProjectCfg).GUID) + { + IVsBuildableProjectCfg buildableConfig; + this.get_BuildableProjectCfg(out buildableConfig); + ppCfg = Marshal.GetComInterfaceForObject(buildableConfig, typeof(IVsBuildableProjectCfg)); + } + + // If not supported + if(ppCfg == IntPtr.Zero) + return VSConstants.E_NOINTERFACE; + + return VSConstants.S_OK; + } + + #endregion + } + + //============================================================================= + // NOTE: advises on out of proc build execution to maximize + // future cross-platform targeting capabilities of the VS tools. + + [CLSCompliant(false)] + [ComVisible(true)] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Buildable")] + public class BuildableProjectConfig : IVsBuildableProjectCfg + { + #region fields + ProjectConfig config = null; + EventSinkCollection callbacks = new EventSinkCollection(); + #endregion + + #region ctors + public BuildableProjectConfig(ProjectConfig config) + { + this.config = config; + } + #endregion + + #region IVsBuildableProjectCfg methods + + public virtual int AdviseBuildStatusCallback(IVsBuildStatusCallback callback, out uint cookie) + { + CCITracing.TraceCall(); + + cookie = callbacks.Add(callback); + return VSConstants.S_OK; + } + + public virtual int get_ProjectCfg(out IVsProjectCfg p) + { + CCITracing.TraceCall(); + + p = config; + return VSConstants.S_OK; + } + + public virtual int QueryStartBuild(uint options, int[] supported, int[] ready) + { + CCITracing.TraceCall(); + if(supported != null && supported.Length > 0) + supported[0] = 1; + if(ready != null && ready.Length > 0) + ready[0] = (this.config.ProjectMgr.BuildInProgress) ? 0 : 1; + return VSConstants.S_OK; + } + + public virtual int QueryStartClean(uint options, int[] supported, int[] ready) + { + CCITracing.TraceCall(); + config.PrepareBuild(false); + if(supported != null && supported.Length > 0) + supported[0] = 1; + if(ready != null && ready.Length > 0) + ready[0] = (this.config.ProjectMgr.BuildInProgress) ? 0 : 1; + return VSConstants.S_OK; + } + + public virtual int QueryStartUpToDateCheck(uint options, int[] supported, int[] ready) + { + CCITracing.TraceCall(); + config.PrepareBuild(false); + if(supported != null && supported.Length > 0) + supported[0] = 0; // TODO: + if(ready != null && ready.Length > 0) + ready[0] = (this.config.ProjectMgr.BuildInProgress) ? 0 : 1; + return VSConstants.S_OK; + } + + public virtual int QueryStatus(out int done) + { + CCITracing.TraceCall(); + + done = (this.config.ProjectMgr.BuildInProgress) ? 0 : 1; + return VSConstants.S_OK; + } + + public virtual int StartBuild(IVsOutputWindowPane pane, uint options) + { + CCITracing.TraceCall(); + config.PrepareBuild(false); + + // Current version of MSBuild wish to be called in an STA + uint flags = VSConstants.VS_BUILDABLEPROJECTCFGOPTS_REBUILD; + + // If we are not asked for a rebuild, then we build the default target (by passing null) + this.Build(options, pane, ((options & flags) != 0) ? MsBuildTarget.Rebuild : null); + + return VSConstants.S_OK; + } + + public virtual int StartClean(IVsOutputWindowPane pane, uint options) + { + CCITracing.TraceCall(); + config.PrepareBuild(true); + // Current version of MSBuild wish to be called in an STA + this.Build(options, pane, MsBuildTarget.Clean); + return VSConstants.S_OK; + } + + public virtual int StartUpToDateCheck(IVsOutputWindowPane pane, uint options) + { + CCITracing.TraceCall(); + + return VSConstants.E_NOTIMPL; + } + + public virtual int Stop(int fsync) + { + CCITracing.TraceCall(); + + return VSConstants.S_OK; + } + + public virtual int UnadviseBuildStatusCallback(uint cookie) + { + CCITracing.TraceCall(); + + + callbacks.RemoveAt(cookie); + return VSConstants.S_OK; + } + + public virtual int Wait(uint ms, int fTickWhenMessageQNotEmpty) + { + CCITracing.TraceCall(); + + return VSConstants.E_NOTIMPL; + } + #endregion + + #region helpers + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private bool NotifyBuildBegin() + { + int shouldContinue = 1; + foreach (IVsBuildStatusCallback cb in callbacks) + { + try + { + ErrorHandler.ThrowOnFailure(cb.BuildBegin(ref shouldContinue)); + if (shouldContinue == 0) + { + return false; + } + } + catch (Exception e) + { + // If those who ask for status have bugs in their code it should not prevent the build/notification from happening + Debug.Fail(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.BuildEventError, CultureInfo.CurrentUICulture), e.Message)); + } + } + + return true; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + private void NotifyBuildEnd(MSBuildResult result, string buildTarget) + { + int success = ((result == MSBuildResult.Successful) ? 1 : 0); + + foreach (IVsBuildStatusCallback cb in callbacks) + { + try + { + ErrorHandler.ThrowOnFailure(cb.BuildEnd(success)); + } + catch (Exception e) + { + // If those who ask for status have bugs in their code it should not prevent the build/notification from happening + Debug.Fail(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.BuildEventError, CultureInfo.CurrentUICulture), e.Message)); + } + finally + { + // We want to refresh the references if we are building with the Build or Rebuild target or if the project was opened for browsing only. + bool shouldRepaintReferences = (buildTarget == null || buildTarget == MsBuildTarget.Build || buildTarget == MsBuildTarget.Rebuild); + + // Now repaint references if that is needed. + // We hardly rely here on the fact the ResolveAssemblyReferences target has been run as part of the build. + // One scenario to think at is when an assembly reference is renamed on disk thus becomming unresolvable, + // but msbuild can actually resolve it. + // Another one if the project was opened only for browsing and now the user chooses to build or rebuild. + if (shouldRepaintReferences && (result == MSBuildResult.Successful)) + { + this.RefreshReferences(); + } + } + } + } + + private void Build(uint options, IVsOutputWindowPane output, string target) + { + if (!this.NotifyBuildBegin()) + { + return; + } + + try + { + config.ProjectMgr.BuildAsync(options, this.config.ConfigName, output, target, (result, buildTarget) => this.NotifyBuildEnd(result, buildTarget)); + } + catch(Exception e) + { + Trace.WriteLine("Exception : " + e.Message); + ErrorHandler.ThrowOnFailure(output.OutputStringThreadSafe("Unhandled Exception:" + e.Message + "\n")); + this.NotifyBuildEnd(MSBuildResult.Failed, target); + throw; + } + finally + { + ErrorHandler.ThrowOnFailure(output.FlushToTaskList()); + } + } + + /// + /// Refreshes references and redraws them correctly. + /// + private void RefreshReferences() + { + // Refresh the reference container node for assemblies that could be resolved. + IReferenceContainer referenceContainer = this.config.ProjectMgr.GetReferenceContainer(); + foreach(ReferenceNode referenceNode in referenceContainer.EnumReferences()) + { + referenceNode.RefreshReference(); + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectContainerNode.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectContainerNode.cs new file mode 100644 index 0000000000..78ae2ec0e8 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectContainerNode.cs @@ -0,0 +1,808 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.Project.Automation; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using MSBuild = Microsoft.Build.Evaluation; + +namespace Microsoft.VisualStudio.Project +{ + [CLSCompliant(false), ComVisible(true)] + public abstract class ProjectContainerNode : ProjectNode, + IVsParentProject, + IBuildDependencyOnProjectContainer + { + #region fields + + /// + /// Setting this flag to true will build all nested project when building this project + /// + private bool buildNestedProjectsOnBuild = true; + + private ProjectElement nestedProjectElement; + + /// + /// Defines the listener that would listen on file changes on the nested project node. + /// + /// + ///This might need a refactoring when nested projects can be added and removed by demand. + /// + private FileChangeManager nestedProjectNodeReloader; + #endregion + + #region ctors + protected ProjectContainerNode() + { + } + #endregion + + #region properties + /// + /// Returns teh object that handles listening to file changes on the nested project files. + /// + internal FileChangeManager NestedProjectNodeReloader + { + get + { + if(this.nestedProjectNodeReloader == null) + { + this.nestedProjectNodeReloader = new FileChangeManager(this.Site); + this.nestedProjectNodeReloader.FileChangedOnDisk += this.OnNestedProjectFileChangedOnDisk; + } + + return this.nestedProjectNodeReloader; + } + } + #endregion + + #region overridden properties + /// + /// This is the object that will be returned by EnvDTE.Project.Object for this project + /// + internal override object Object + { + get { return new OASolutionFolder(this); } + } + + #endregion + + #region public overridden methods + /// + /// Gets the nested hierarchy. + /// + /// The item id. + /// Identifier of the interface to be returned in ppHierarchyNested. + /// Pointer to the interface whose identifier was passed in iidHierarchyNested. + /// Pointer to an item identifier of the root node of the nested hierarchy. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. If ITEMID is not a nested hierarchy, this method returns E_FAIL. + [CLSCompliant(false)] + public override int GetNestedHierarchy(UInt32 itemId, ref Guid iidHierarchyNested, out IntPtr ppHierarchyNested, out uint pItemId) + { + pItemId = VSConstants.VSITEMID_ROOT; + ppHierarchyNested = IntPtr.Zero; + if(this.FirstChild != null) + { + for(HierarchyNode n = this.FirstChild; n != null; n = n.NextSibling) + { + NestedProjectNode p = n as NestedProjectNode; + + if(p != null && p.ID == itemId) + { + if(p.NestedHierarchy != null) + { + IntPtr iunknownPtr = IntPtr.Zero; + int returnValue = VSConstants.S_OK; + try + { + iunknownPtr = Marshal.GetIUnknownForObject(p.NestedHierarchy); + Marshal.QueryInterface(iunknownPtr, ref iidHierarchyNested, out ppHierarchyNested); + } + catch(COMException e) + { + returnValue = e.ErrorCode; + } + finally + { + if(iunknownPtr != IntPtr.Zero) + { + Marshal.Release(iunknownPtr); + } + } + + return returnValue; + } + break; + } + } + } + + return VSConstants.E_FAIL; + } + + public override int IsItemDirty(uint itemId, IntPtr punkDocData, out int pfDirty) + { + HierarchyNode hierNode = this.NodeFromItemId(itemId); + Debug.Assert(hierNode != null, "Hierarchy node not found"); + if(hierNode != this) + { + return ErrorHandler.ThrowOnFailure(hierNode.IsItemDirty(itemId, punkDocData, out pfDirty)); + } + else + { + return ErrorHandler.ThrowOnFailure(base.IsItemDirty(itemId, punkDocData, out pfDirty)); + } + } + + public override int SaveItem(VSSAVEFLAGS dwSave, string silentSaveAsName, uint itemid, IntPtr punkDocData, out int pfCancelled) + { + HierarchyNode hierNode = this.NodeFromItemId(itemid); + Debug.Assert(hierNode != null, "Hierarchy node not found"); + if(hierNode != this) + { + return ErrorHandler.ThrowOnFailure(hierNode.SaveItem(dwSave, silentSaveAsName, itemid, punkDocData, out pfCancelled)); + } + else + { + return ErrorHandler.ThrowOnFailure(base.SaveItem(dwSave, silentSaveAsName, itemid, punkDocData, out pfCancelled)); + } + } + + protected override bool FilterItemTypeToBeAddedToHierarchy(string itemType) + { + if(String.Compare(itemType, ProjectFileConstants.SubProject, StringComparison.OrdinalIgnoreCase) == 0) + { + return true; + } + return base.FilterItemTypeToBeAddedToHierarchy(itemType); + } + + /// + /// Called to reload a project item. + /// Reloads a project and its nested project nodes. + /// + /// Specifies itemid from VSITEMID. + /// Reserved. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public override int ReloadItem(uint itemId, uint reserved) + { + #region precondition + if(this.IsClosed) + { + return VSConstants.E_FAIL; + } + #endregion + + NestedProjectNode node = this.NodeFromItemId(itemId) as NestedProjectNode; + + if(node != null) + { + object propertyAsObject = node.GetProperty((int)__VSHPROPID.VSHPROPID_HandlesOwnReload); + + if(propertyAsObject != null && (bool)propertyAsObject) + { + node.ReloadItem(reserved); + } + else + { + this.ReloadNestedProjectNode(node); + } + + return VSConstants.S_OK; + } + + return base.ReloadItem(itemId, reserved); + } + + /// + /// Reloads a project and its nested project nodes. + /// + protected override void Reload() + { + base.Reload(); + this.CreateNestedProjectNodes(); + } + #endregion + + #region IVsParentProject + public virtual int OpenChildren() + { + IVsSolution solution = this.GetService(typeof(IVsSolution)) as IVsSolution; + + Debug.Assert(solution != null, "Could not retrieve the solution from the services provided by this project"); + if(solution == null) + { + return VSConstants.E_FAIL; + } + + IntPtr iUnKnownForSolution = IntPtr.Zero; + int returnValue = VSConstants.S_OK; // be optimistic. + + try + { + this.DisableQueryEdit = true; + this.EventTriggeringFlag = ProjectNode.EventTriggering.DoNotTriggerHierarchyEvents | ProjectNode.EventTriggering.DoNotTriggerTrackerEvents; + iUnKnownForSolution = Marshal.GetIUnknownForObject(solution); + + // notify SolutionEvents listeners that we are about to add children + IVsFireSolutionEvents fireSolutionEvents = Marshal.GetTypedObjectForIUnknown(iUnKnownForSolution, typeof(IVsFireSolutionEvents)) as IVsFireSolutionEvents; + ErrorHandler.ThrowOnFailure(fireSolutionEvents.FireOnBeforeOpeningChildren(this)); + + this.AddVirtualProjects(); + + ErrorHandler.ThrowOnFailure(fireSolutionEvents.FireOnAfterOpeningChildren(this)); + } + catch(Exception e) + { + // Exceptions are digested by the caller but we want then to be shown if not a ComException and if not in automation. + if(!(e is COMException) && !Utilities.IsInAutomationFunction(this.Site)) + { + string title = null; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + VsShellUtilities.ShowMessageBox(this.Site, title, e.Message, icon, buttons, defaultButton); + } + + Trace.WriteLine("Exception : " + e.Message); + throw; + } + finally + { + this.DisableQueryEdit = false; + + if(iUnKnownForSolution != IntPtr.Zero) + { + Marshal.Release(iUnKnownForSolution); + } + + this.EventTriggeringFlag = ProjectNode.EventTriggering.TriggerAll; + } + + return returnValue; + } + + public virtual int CloseChildren() + { + int returnValue = VSConstants.S_OK; // be optimistic. + + IVsSolution solution = this.GetService(typeof(IVsSolution)) as IVsSolution; + Debug.Assert(solution != null, "Could not retrieve the solution from the services provided by this project"); + + if(solution == null) + { + return VSConstants.E_FAIL; + } + + IntPtr iUnKnownForSolution = IntPtr.Zero; + + try + { + iUnKnownForSolution = Marshal.GetIUnknownForObject(solution); + + // notify SolutionEvents listeners that we are about to close children + IVsFireSolutionEvents fireSolutionEvents = Marshal.GetTypedObjectForIUnknown(iUnKnownForSolution, typeof(IVsFireSolutionEvents)) as IVsFireSolutionEvents; + ErrorHandler.ThrowOnFailure(fireSolutionEvents.FireOnBeforeClosingChildren(this)); + + // If the removal crashes we never fire the close children event. IS that a problem? + this.RemoveNestedProjectNodes(); + + ErrorHandler.ThrowOnFailure(fireSolutionEvents.FireOnAfterClosingChildren(this)); + } + finally + { + if(iUnKnownForSolution != IntPtr.Zero) + { + Marshal.Release(iUnKnownForSolution); + } + } + + return returnValue; + } + #endregion + + #region IBuildDependencyOnProjectContainerNode + /// + /// Defines whether nested projects should be build with the parent project + /// + public virtual bool BuildNestedProjectsOnBuild + { + get { return this.buildNestedProjectsOnBuild; } + set { this.buildNestedProjectsOnBuild = value; } + } + + /// + /// Enumerates the nested hierachies that should be added to the build dependency list. + /// + /// + public virtual IVsHierarchy[] EnumNestedHierachiesForBuildDependency() + { + List nestedProjectList = new List(); + // Add all nested project among projectContainer child nodes + for(HierarchyNode child = this.FirstChild; child != null; child = child.NextSibling) + { + NestedProjectNode nestedProjectNode = child as NestedProjectNode; + if(nestedProjectNode != null) + { + nestedProjectList.Add(nestedProjectNode.NestedHierarchy); + } + } + + return nestedProjectList.ToArray(); + } + #endregion + + #region helper methods + + internal protected void RemoveNestedProjectNodes() + { + for(HierarchyNode n = this.FirstChild; n != null; n = n.NextSibling) + { + NestedProjectNode p = n as NestedProjectNode; + if(p != null) + { + p.CloseNestedProjectNode(); + } + } + + // We do not care of file changes after this. + this.NestedProjectNodeReloader.FileChangedOnDisk -= this.OnNestedProjectFileChangedOnDisk; + this.NestedProjectNodeReloader.Dispose(); + } + + /// + /// This is used when loading the project to loop through all the items + /// and for each SubProject it finds, it create the project and a node + /// in our Hierarchy to hold the project. + /// + internal protected void CreateNestedProjectNodes() + { + // 1. Create a ProjectElement with the found item and then Instantiate a new Nested project with this ProjectElement. + // 2. Link into the hierarchy. + // Read subprojects from from msbuildmodel + __VSCREATEPROJFLAGS creationFlags = __VSCREATEPROJFLAGS.CPF_NOTINSLNEXPLR | __VSCREATEPROJFLAGS.CPF_SILENT; + + if(this.IsNewProject) + { + creationFlags |= __VSCREATEPROJFLAGS.CPF_CLONEFILE; + } + else + { + creationFlags |= __VSCREATEPROJFLAGS.CPF_OPENFILE; + } + + foreach (MSBuild.ProjectItem item in this.BuildProject.Items) + { + if(String.Compare(item.ItemType, ProjectFileConstants.SubProject, StringComparison.OrdinalIgnoreCase) == 0) + { + this.nestedProjectElement = new ProjectElement(this, item, false); + + if(!this.IsNewProject) + { + AddExistingNestedProject(null, creationFlags); + } + else + { + // If we are creating the subproject from a vstemplate/vsz file + bool isVsTemplate = Utilities.IsTemplateFile(GetProjectTemplatePath(null)); + if(isVsTemplate) + { + RunVsTemplateWizard(null, true); + } + else + { + // We are cloning the specified project file + AddNestedProjectFromTemplate(null, creationFlags); + } + } + } + } + + this.nestedProjectElement = null; + } + /// + /// Add an existing project as a nested node of our hierarchy. + /// This is used while loading the project and can also be used + /// to add an existing project to our hierarchy. + /// + protected internal virtual NestedProjectNode AddExistingNestedProject(ProjectElement element, __VSCREATEPROJFLAGS creationFlags) + { + ProjectElement elementToUse = (element == null) ? this.nestedProjectElement : element; + + if(elementToUse == null) + { + throw new ArgumentNullException("element"); + } + + string filename = elementToUse.GetFullPathForElement(); + // Delegate to AddNestedProjectFromTemplate. Because we pass flags that specify open project rather then clone, this will works. + Debug.Assert((creationFlags & __VSCREATEPROJFLAGS.CPF_OPENFILE) == __VSCREATEPROJFLAGS.CPF_OPENFILE, "__VSCREATEPROJFLAGS.CPF_OPENFILE should have been specified, did you mean to call AddNestedProjectFromTemplate?"); + return AddNestedProjectFromTemplate(filename, Path.GetDirectoryName(filename), Path.GetFileName(filename), elementToUse, creationFlags); + } + + /// + /// Let the wizard code execute and provide us the information we need. + /// Our SolutionFolder automation object should be called back with the + /// details at which point it will call back one of our method to add + /// a nested project. + /// If you are trying to add a new subproject this is probably the + /// method you want to call. If you are just trying to clone a template + /// project file, then AddNestedProjectFromTemplate is what you want. + /// + /// The project item to use as the base of the nested project. + /// true if the wizard should run silently, otherwise false. + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Vs")] + protected internal void RunVsTemplateWizard(ProjectElement element, bool silent) + { + ProjectElement elementToUse = (element == null) ? this.nestedProjectElement : element; + + if(elementToUse == null) + { + throw new ArgumentNullException("element"); + } + this.nestedProjectElement = elementToUse; + + Automation.OAProject oaProject = GetAutomationObject() as Automation.OAProject; + if(oaProject == null || oaProject.ProjectItems == null) + throw new System.InvalidOperationException(SR.GetString(SR.InvalidAutomationObject, CultureInfo.CurrentUICulture)); + Debug.Assert(oaProject.Object != null, "The project automation object should have set the Object to the SolutionFolder"); + Automation.OASolutionFolder folder = oaProject.Object as Automation.OASolutionFolder; + + // Prepare the parameters to pass to RunWizardFile + string destination = elementToUse.GetFullPathForElement(); + string template = this.GetProjectTemplatePath(elementToUse); + + object[] wizParams = new object[7]; + wizParams[0] = EnvDTE.Constants.vsWizardAddSubProject; + wizParams[1] = Path.GetFileNameWithoutExtension(destination); + wizParams[2] = oaProject.ProjectItems; + wizParams[3] = Path.GetDirectoryName(destination); + wizParams[4] = Path.GetFileNameWithoutExtension(destination); + wizParams[5] = Path.GetDirectoryName(folder.DTE.FullName); //VS install dir + wizParams[6] = silent; + + IVsDetermineWizardTrust wizardTrust = this.GetService(typeof(SVsDetermineWizardTrust)) as IVsDetermineWizardTrust; + if(wizardTrust != null) + { + Guid guidProjectAdding = Guid.Empty; + + // In case of a project template an empty guid should be added as the guid parameter. See env\msenv\core\newtree.h IsTrustedTemplate method definition. + ErrorHandler.ThrowOnFailure(wizardTrust.OnWizardInitiated(template, ref guidProjectAdding)); + } + + try + { + // Make the call to execute the wizard. This should cause AddNestedProjectFromTemplate to be + // called back with the correct set of parameters. + EnvDTE.IVsExtensibility extensibilityService = (EnvDTE.IVsExtensibility)GetService(typeof(EnvDTE.IVsExtensibility)); + EnvDTE.wizardResult result = extensibilityService.RunWizardFile(template, 0, ref wizParams); + if(result == EnvDTE.wizardResult.wizardResultFailure) + throw new COMException(); + } + finally + { + if(wizardTrust != null) + { + ErrorHandler.ThrowOnFailure(wizardTrust.OnWizardCompleted()); + } + } + } + + /// + /// This will clone a template project file and add it as a + /// subproject to our hierarchy. + /// If you want to create a project for which there exist a + /// vstemplate, consider using RunVsTemplateWizard instead. + /// + protected internal virtual NestedProjectNode AddNestedProjectFromTemplate(ProjectElement element, __VSCREATEPROJFLAGS creationFlags) + { + ProjectElement elementToUse = (element == null) ? this.nestedProjectElement : element; + + if(elementToUse == null) + { + throw new ArgumentNullException("element"); + } + string destination = elementToUse.GetFullPathForElement(); + string template = this.GetProjectTemplatePath(elementToUse); + + return this.AddNestedProjectFromTemplate(template, Path.GetDirectoryName(destination), Path.GetFileName(destination), elementToUse, creationFlags); + } + + /// + /// This can be called directly or through RunVsTemplateWizard. + /// This will clone a template project file and add it as a + /// subproject to our hierarchy. + /// If you want to create a project for which there exist a + /// vstemplate, consider using RunVsTemplateWizard instead. + /// + protected internal virtual NestedProjectNode AddNestedProjectFromTemplate(string fileName, string destination, string projectName, ProjectElement element, __VSCREATEPROJFLAGS creationFlags) + { + // If this is project creation and the template specified a subproject in its project file, this.nestedProjectElement will be used + ProjectElement elementToUse = (element == null) ? this.nestedProjectElement : element; + + if(elementToUse == null) + { + // If this is null, this means MSBuild does not know anything about our subproject so add an MSBuild item for it + elementToUse = new ProjectElement(this, fileName, ProjectFileConstants.SubProject); + } + + NestedProjectNode node = CreateNestedProjectNode(elementToUse); + node.Init(fileName, destination, projectName, creationFlags); + + // In case that with did not have an existing element, or the nestedProjectelement was null + // and since Init computes the final path, set the MSBuild item to that path + if(this.nestedProjectElement == null) + { + string relativePath = node.Url; + if(Path.IsPathRooted(relativePath)) + { + relativePath = this.ProjectFolder; + if(!relativePath.EndsWith("/\\", StringComparison.Ordinal)) + { + relativePath += Path.DirectorySeparatorChar; + } + + relativePath = new Url(relativePath).MakeRelative(new Url(node.Url)); + } + + elementToUse.Rename(relativePath); + } + + this.AddChild(node); + return node; + } + + /// + /// Override this method if you want to provide your own type of nodes. + /// This would be the case if you derive a class from NestedProjectNode + /// + protected virtual NestedProjectNode CreateNestedProjectNode(ProjectElement element) + { + return new NestedProjectNode(this, element); + } + + /// + /// Links the nested project nodes to the solution. The default implementation parses all nested project nodes and calles AddVirtualProjectEx on them. + /// + protected virtual void AddVirtualProjects() + { + for(HierarchyNode child = this.FirstChild; child != null; child = child.NextSibling) + { + NestedProjectNode nestedProjectNode = child as NestedProjectNode; + if(nestedProjectNode != null) + { + nestedProjectNode.AddVirtualProject(); + } + } + } + + /// + /// Based on the Template and TypeGuid properties of the + /// element, generate the full template path. + /// + /// TypeGuid should be the Guid of a registered project factory. + /// Template can be a full path, a project template (for projects + /// that support VsTemplates) or a relative path (for other projects). + /// + protected virtual string GetProjectTemplatePath(ProjectElement element) + { + ProjectElement elementToUse = (element == null) ? this.nestedProjectElement : element; + + if(elementToUse == null) + { + throw new ArgumentNullException("element"); + } + + string templateFile = elementToUse.GetMetadata(ProjectFileConstants.Template); + Debug.Assert(!String.IsNullOrEmpty(templateFile), "No template file has been specified in the template attribute in the project file"); + + string fullPath = templateFile; + if(!Path.IsPathRooted(templateFile)) + { + RegisteredProjectType registeredProjectType = this.GetRegisteredProject(elementToUse); + + // This is not a full path + Debug.Assert(registeredProjectType != null && (!String.IsNullOrEmpty(registeredProjectType.DefaultProjectExtensionValue) || !String.IsNullOrEmpty(registeredProjectType.WizardTemplatesDirValue)), " Registered wizard directory value not set in the registry."); + + // See if this specify a VsTemplate file + fullPath = registeredProjectType.GetVsTemplateFile(templateFile); + if(String.IsNullOrEmpty(fullPath)) + { + // Default to using the WizardTemplateDir to calculate the absolute path + fullPath = Path.Combine(registeredProjectType.WizardTemplatesDirValue, templateFile); + } + } + + return fullPath; + } + + /// + /// Get information from the registry based for the project + /// factory corresponding to the TypeGuid of the element + /// + private RegisteredProjectType GetRegisteredProject(ProjectElement element) + { + ProjectElement elementToUse = (element == null) ? this.nestedProjectElement : element; + + if(elementToUse == null) + { + throw new ArgumentNullException("element"); + } + + // Get the project type guid from project elementToUse + string typeGuidString = elementToUse.GetMetadataAndThrow(ProjectFileConstants.TypeGuid, new Exception()); + Guid projectFactoryGuid = new Guid(typeGuidString); + + EnvDTE.DTE dte = this.ProjectMgr.Site.GetService(typeof(EnvDTE.DTE)) as EnvDTE.DTE; + Debug.Assert(dte != null, "Could not get the automation object from the services exposed by this project"); + + if(dte == null) + throw new InvalidOperationException(); + + RegisteredProjectType registeredProjectType = RegisteredProjectType.CreateRegisteredProjectType(projectFactoryGuid); + Debug.Assert(registeredProjectType != null, "Could not read the registry setting associated to this project."); + if(registeredProjectType == null) + { + throw new InvalidOperationException(); + } + return registeredProjectType; + } + + /// + /// Reloads a nested project node by deleting it and readding it. + /// + /// The node to reload. + protected virtual void ReloadNestedProjectNode(NestedProjectNode node) + { + if(node == null) + { + throw new ArgumentNullException("node"); + } + + IVsSolution solution = this.GetService(typeof(IVsSolution)) as IVsSolution; + + if(solution == null) + { + throw new InvalidOperationException(); + } + + NestedProjectNode newNode = null; + try + { + // (VS 2005 UPDATE) When deleting and re-adding the nested project, + // we do not want SCC to see this as a delete and add operation. + this.EventTriggeringFlag = ProjectNode.EventTriggering.DoNotTriggerTrackerEvents; + + // notify SolutionEvents listeners that we are about to add children + IVsFireSolutionEvents fireSolutionEvents = solution as IVsFireSolutionEvents; + + if(fireSolutionEvents == null) + { + throw new InvalidOperationException(); + } + + ErrorHandler.ThrowOnFailure(fireSolutionEvents.FireOnBeforeUnloadProject(node.NestedHierarchy)); + + int isDirtyAsInt = 0; + this.IsDirty(out isDirtyAsInt); + + bool isDirty = (isDirtyAsInt == 0) ? false : true; + + ProjectElement element = node.ItemNode; + node.CloseNestedProjectNode(); + + // Remove from the solution + this.RemoveChild(node); + + // Now readd it + try + { + __VSCREATEPROJFLAGS flags = __VSCREATEPROJFLAGS.CPF_NOTINSLNEXPLR | __VSCREATEPROJFLAGS.CPF_SILENT | __VSCREATEPROJFLAGS.CPF_OPENFILE; + newNode = this.AddExistingNestedProject(element, flags); + newNode.AddVirtualProject(); + } + catch(Exception e) + { + // We get a System.Exception if anything failed, thus we have no choice but catch it. + // Exceptions are digested by VS. Show the error if not in automation. + if(!Utilities.IsInAutomationFunction(this.Site)) + { + string message = (String.IsNullOrEmpty(e.Message)) ? SR.GetString(SR.NestedProjectFailedToReload, CultureInfo.CurrentUICulture) : e.Message; + string title = string.Empty; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton); + } + + // Do not digest exception. let the caller handle it. If in a later stage this exception is not digested then the above messagebox is not needed. + throw; + } + +#if DEBUG + IVsHierarchy nestedHierarchy; + ErrorHandler.ThrowOnFailure(solution.GetProjectOfUniqueName(newNode.GetMkDocument(), out nestedHierarchy)); + Debug.Assert(nestedHierarchy != null && Utilities.IsSameComObject(nestedHierarchy, newNode.NestedHierarchy), "The nested hierrachy was not reloaded correctly."); +#endif + this.SetProjectFileDirty(isDirty); + + ErrorHandler.ThrowOnFailure(fireSolutionEvents.FireOnAfterLoadProject(newNode.NestedHierarchy)); + } + finally + { + // In this scenario the nested project failed to unload or reload the nested project. We will unload the whole project, otherwise the nested project is lost. + // This is similar to the scenario when one wants to open a project and the nested project cannot be loaded because for example the project file has xml errors. + // We should note that we rely here that if the unload fails then exceptions are not digested and are shown to the user. + if(newNode == null || newNode.NestedHierarchy == null) + { + ErrorHandler.ThrowOnFailure(solution.CloseSolutionElement((uint)__VSSLNCLOSEOPTIONS.SLNCLOSEOPT_UnloadProject | (uint)__VSSLNSAVEOPTIONS.SLNSAVEOPT_ForceSave, this, 0)); + } + else + { + this.EventTriggeringFlag = ProjectNode.EventTriggering.TriggerAll; + } + } + } + + /// + /// Event callback. Called when one of the nested project files is changed. + /// + /// The FileChangeManager object. + /// Event args containing the file name that was updated. + private void OnNestedProjectFileChangedOnDisk(object sender, FileChangedOnDiskEventArgs e) + { + #region Pre-condition validation + Debug.Assert(e != null, "No event args specified for the FileChangedOnDisk event"); + + // We care only about time change for reload. + if((e.FileChangeFlag & _VSFILECHANGEFLAGS.VSFILECHG_Time) == 0) + { + return; + } + + // test if we actually have a document for this id. + string moniker; + this.GetMkDocument(e.ItemID, out moniker); + Debug.Assert(NativeMethods.IsSamePath(moniker, e.FileName), " The file + " + e.FileName + " has changed but we could not retrieve the path for the item id associated to the path."); + #endregion + + bool reload = true; + if(!Utilities.IsInAutomationFunction(this.Site)) + { + // Prompt to reload the nested project file. We use the moniker here since the filename from the event arg is canonicalized. + string message = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.QueryReloadNestedProject, CultureInfo.CurrentUICulture), moniker); + string title = string.Empty; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_INFO; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_YESNO; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + reload = (VsShellUtilities.ShowMessageBox(this.Site, message, title, icon, buttons, defaultButton) == NativeMethods.IDYES); + } + + if(reload) + { + // We have to use here the interface method call, since it might be that specialized project nodes like the project container item + // is owerwriting the default functionality. + this.ReloadItem(e.ItemID, 0); + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectDesignerDocumentManager.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectDesignerDocumentManager.cs new file mode 100644 index 0000000000..8727d4430c --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectDesignerDocumentManager.cs @@ -0,0 +1,79 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; + +namespace Microsoft.VisualStudio.Project +{ + class ProjectDesignerDocumentManager : DocumentManager + { + #region ctors + public ProjectDesignerDocumentManager(ProjectNode node) + : base(node) + { + } + #endregion + + #region overriden methods + + public override int Open(ref Guid logicalView, IntPtr docDataExisting, out IVsWindowFrame windowFrame, WindowFrameShowAction windowFrameAction) + { + Guid editorGuid = VSConstants.GUID_ProjectDesignerEditor; + return this.OpenWithSpecific(0, ref editorGuid, String.Empty, ref logicalView, docDataExisting, out windowFrame, windowFrameAction); + } + + public override int OpenWithSpecific(uint editorFlags, ref Guid editorType, string physicalView, ref Guid logicalView, IntPtr docDataExisting, out IVsWindowFrame frame, WindowFrameShowAction windowFrameAction) + { + frame = null; + Debug.Assert(editorType == VSConstants.GUID_ProjectDesignerEditor, "Cannot open project designer with guid " + editorType.ToString()); + + + if(this.Node == null || this.Node.ProjectMgr == null || this.Node.ProjectMgr.IsClosed) + { + return VSConstants.E_FAIL; + } + + IVsUIShellOpenDocument uiShellOpenDocument = this.Node.ProjectMgr.Site.GetService(typeof(SVsUIShellOpenDocument)) as IVsUIShellOpenDocument; + IOleServiceProvider serviceProvider = this.Node.ProjectMgr.Site.GetService(typeof(IOleServiceProvider)) as IOleServiceProvider; + + if(serviceProvider != null && uiShellOpenDocument != null) + { + string fullPath = this.GetFullPathForDocument(); + string caption = this.GetOwnerCaption(); + + IVsUIHierarchy parentHierarchy = this.Node.ProjectMgr.GetProperty((int)__VSHPROPID.VSHPROPID_ParentHierarchy) as IVsUIHierarchy; + + IntPtr parentHierarchyItemId = (IntPtr)this.Node.ProjectMgr.GetProperty((int)__VSHPROPID.VSHPROPID_ParentHierarchyItemid); + + ErrorHandler.ThrowOnFailure(uiShellOpenDocument.OpenSpecificEditor(editorFlags, fullPath, ref editorType, physicalView, ref logicalView, caption, parentHierarchy, (uint)(parentHierarchyItemId.ToInt32()), docDataExisting, serviceProvider, out frame)); + + if(frame != null) + { + if(windowFrameAction == WindowFrameShowAction.Show) + { + ErrorHandler.ThrowOnFailure(frame.Show()); + } + } + } + + return VSConstants.S_OK; + } + #endregion + + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectDocumentsListener.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectDocumentsListener.cs new file mode 100644 index 0000000000..2eaeb76350 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectDocumentsListener.cs @@ -0,0 +1,199 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ +using System; +using System.Diagnostics; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using ShellConstants = Microsoft.VisualStudio.Shell.Interop.Constants; +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.VisualStudio.Project +{ + + [CLSCompliant(false)] + public abstract class ProjectDocumentsListener : IVsTrackProjectDocumentsEvents2, IDisposable + { + #region fields + private uint eventsCookie; + private IVsTrackProjectDocuments2 projectDocTracker; + private ServiceProvider serviceProvider; + private bool isDisposed; + /// + /// Defines an object that will be a mutex for this object for synchronizing thread calls. + /// + private static volatile object Mutex = new object(); + #endregion + + #region ctors + protected ProjectDocumentsListener(ServiceProvider serviceProviderParameter) + { + if (serviceProviderParameter == null) + { + throw new ArgumentNullException("serviceProviderParameter"); + } + + this.serviceProvider = serviceProviderParameter; + this.projectDocTracker = this.serviceProvider.GetService(typeof(SVsTrackProjectDocuments)) as IVsTrackProjectDocuments2; + + Debug.Assert(this.projectDocTracker != null, "Could not get the IVsTrackProjectDocuments2 object from the services exposed by this project"); + + if(this.projectDocTracker == null) + { + throw new InvalidOperationException(); + } + } + #endregion + + #region properties + protected uint EventsCookie + { + get + { + return this.eventsCookie; + } + } + + protected IVsTrackProjectDocuments2 ProjectDocumentTracker2 + { + get + { + return this.projectDocTracker; + } + } + + protected ServiceProvider ServiceProvider + { + get + { + return this.serviceProvider; + } + } + #endregion + + #region IVsTrackProjectDocumentsEvents2 Members + + public virtual int OnAfterAddDirectoriesEx(int cProjects, int cDirectories, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSADDDIRECTORYFLAGS[] rgFlags) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnAfterAddFilesEx(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSADDFILEFLAGS[] rgFlags) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnAfterRemoveDirectories(int cProjects, int cDirectories, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSREMOVEDIRECTORYFLAGS[] rgFlags) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnAfterRemoveFiles(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, VSREMOVEFILEFLAGS[] rgFlags) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnAfterRenameDirectories(int cProjects, int cDirs, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgszMkOldNames, string[] rgszMkNewNames, VSRENAMEDIRECTORYFLAGS[] rgFlags) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnAfterRenameFiles(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgszMkOldNames, string[] rgszMkNewNames, VSRENAMEFILEFLAGS[] rgFlags) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnAfterSccStatusChanged(int cProjects, int cFiles, IVsProject[] rgpProjects, int[] rgFirstIndices, string[] rgpszMkDocuments, uint[] rgdwSccStatus) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnQueryAddDirectories(IVsProject pProject, int cDirectories, string[] rgpszMkDocuments, VSQUERYADDDIRECTORYFLAGS[] rgFlags, VSQUERYADDDIRECTORYRESULTS[] pSummaryResult, VSQUERYADDDIRECTORYRESULTS[] rgResults) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnQueryAddFiles(IVsProject pProject, int cFiles, string[] rgpszMkDocuments, VSQUERYADDFILEFLAGS[] rgFlags, VSQUERYADDFILERESULTS[] pSummaryResult, VSQUERYADDFILERESULTS[] rgResults) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnQueryRemoveDirectories(IVsProject pProject, int cDirectories, string[] rgpszMkDocuments, VSQUERYREMOVEDIRECTORYFLAGS[] rgFlags, VSQUERYREMOVEDIRECTORYRESULTS[] pSummaryResult, VSQUERYREMOVEDIRECTORYRESULTS[] rgResults) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnQueryRemoveFiles(IVsProject pProject, int cFiles, string[] rgpszMkDocuments, VSQUERYREMOVEFILEFLAGS[] rgFlags, VSQUERYREMOVEFILERESULTS[] pSummaryResult, VSQUERYREMOVEFILERESULTS[] rgResults) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnQueryRenameDirectories(IVsProject pProject, int cDirs, string[] rgszMkOldNames, string[] rgszMkNewNames, VSQUERYRENAMEDIRECTORYFLAGS[] rgFlags, VSQUERYRENAMEDIRECTORYRESULTS[] pSummaryResult, VSQUERYRENAMEDIRECTORYRESULTS[] rgResults) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnQueryRenameFiles(IVsProject pProject, int cFiles, string[] rgszMkOldNames, string[] rgszMkNewNames, VSQUERYRENAMEFILEFLAGS[] rgFlags, VSQUERYRENAMEFILERESULTS[] pSummaryResult, VSQUERYRENAMEFILERESULTS[] rgResults) + { + return VSConstants.E_NOTIMPL; + } + + #endregion + + #region IDisposable Members + /// + /// The IDispose interface Dispose method for disposing the object determinastically. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + #region methods + public void Init() + { + if(this.ProjectDocumentTracker2 != null) + { + ErrorHandler.ThrowOnFailure(this.ProjectDocumentTracker2.AdviseTrackProjectDocumentsEvents(this, out this.eventsCookie)); + } + } + + /// + /// The method that does the cleanup. + /// + /// + [SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "Microsoft.VisualStudio.Shell.Interop.IVsTrackProjectDocuments2.UnadviseTrackProjectDocumentsEvents(System.UInt32)")] + protected virtual void Dispose(bool disposing) + { + // Everybody can go here. + if(!this.isDisposed) + { + // Synchronize calls to the Dispose simulteniously. + lock(Mutex) + { + if(disposing && this.eventsCookie != (uint)ShellConstants.VSCOOKIE_NIL && this.ProjectDocumentTracker2 != null) + { + this.ProjectDocumentTracker2.UnadviseTrackProjectDocumentsEvents((uint)this.eventsCookie); + this.eventsCookie = (uint)ShellConstants.VSCOOKIE_NIL; + } + + this.isDisposed = true; + } + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectElement.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectElement.cs new file mode 100644 index 0000000000..ad3e14b685 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectElement.cs @@ -0,0 +1,422 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using MSBuild = Microsoft.Build.Evaluation; +using Microsoft.Build.Evaluation; + +namespace Microsoft.VisualStudio.Project +{ + + /// + /// This class represent a project item (usualy a file) and allow getting and + /// setting attribute on it. + /// This class allow us to keep the internal details of our items hidden from + /// our derived classes. + /// While the class itself is public so it can be manipulated by derived classes, + /// its internal constructors make sure it can only be created from within the assembly. + /// + public sealed class ProjectElement + { + #region fields + private MSBuild.ProjectItem item; + private ProjectNode itemProject; + private bool deleted; + private bool isVirtual; + private Dictionary virtualProperties; + #endregion + + #region properties + public string ItemName + { + get + { + if(this.HasItemBeenDeleted()) + { + return String.Empty; + } + else + { + return this.item.ItemType; + } + } + set + { + if(!this.HasItemBeenDeleted()) + { + // Check out the project file. + if(!this.itemProject.QueryEditProjectFile(false)) + { + throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + + this.item.ItemType = value; + } + } + } + + internal MSBuild.ProjectItem Item + { + get + { + return this.item; + } + } + + internal bool IsVirtual + { + get + { + return this.isVirtual; + } + } + #endregion + + #region ctors + /// + /// Constructor to create a new MSBuild.ProjectItem and add it to the project + /// Only have internal constructors as the only one who should be creating + /// such object is the project itself (see Project.CreateFileNode()). + /// + internal ProjectElement(ProjectNode project, string itemPath, string itemType) + { + if(project == null) + { + throw new ArgumentNullException("project"); + } + + if(String.IsNullOrEmpty(itemPath)) + { + throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "itemPath"); + } + + + if(String.IsNullOrEmpty(itemType)) + { + throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "itemType"); + } + + this.itemProject = project; + + // create and add the item to the project + + this.item = project.BuildProject.AddItem(itemType, Microsoft.Build.Evaluation.ProjectCollection.Escape(itemPath))[0]; + this.itemProject.SetProjectFileDirty(true); + this.RefreshProperties(); + } + + /// + /// Constructor to Wrap an existing MSBuild.ProjectItem + /// Only have internal constructors as the only one who should be creating + /// such object is the project itself (see Project.CreateFileNode()). + /// + /// Project that owns this item + /// an MSBuild.ProjectItem; can be null if virtualFolder is true + /// Is this item virtual (such as reference folder) + internal ProjectElement(ProjectNode project, MSBuild.ProjectItem existingItem, bool virtualFolder) + { + if(project == null) + throw new ArgumentNullException("project"); + if(!virtualFolder && existingItem == null) + throw new ArgumentNullException("existingItem"); + + // Keep a reference to project and item + this.itemProject = project; + this.item = existingItem; + this.isVirtual = virtualFolder; + + if(this.isVirtual) + this.virtualProperties = new Dictionary(); + } + #endregion + + #region public methods + /// + /// Calling this method remove this item from the project file. + /// Once the item is delete, you should not longer be using it. + /// Note that the item should be removed from the hierarchy prior to this call. + /// + public void RemoveFromProjectFile() + { + if(!deleted && item != null) + { + deleted = true; + itemProject.BuildProject.RemoveItem(item); + } + itemProject = null; + item = null; + } + + /// + /// Set an attribute on the project element + /// + /// Name of the attribute to set + /// Value to give to the attribute + public void SetMetadata(string attributeName, string attributeValue) + { + Debug.Assert(String.Compare(attributeName, ProjectFileConstants.Include, StringComparison.OrdinalIgnoreCase) != 0, "Use rename as this won't work"); + + if(this.IsVirtual) + { + // For virtual node, use our virtual property collection + if(virtualProperties.ContainsKey(attributeName)) + virtualProperties.Remove(attributeName); + virtualProperties.Add(attributeName, attributeValue); + return; + } + + // Build Action is the type, not a property, so intercept + if(String.Compare(attributeName, ProjectFileConstants.BuildAction, StringComparison.OrdinalIgnoreCase) == 0) + { + item.ItemType = attributeValue; + return; + } + + // Check out the project file. + if(!this.itemProject.QueryEditProjectFile(false)) + { + throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + + if(attributeValue == null) + item.RemoveMetadata(attributeName); + else + item.SetMetadataValue(attributeName, attributeValue); + itemProject.SetProjectFileDirty(true); + } + + public string GetEvaluatedMetadata(string attributeName) + { + if(this.IsVirtual) + { + // For virtual items, use our virtual property collection + if(!virtualProperties.ContainsKey(attributeName)) + { + return String.Empty; + } + return virtualProperties[attributeName]; + } + + // cannot ask MSBuild for Include, so intercept it and return the corresponding property + if(String.Compare(attributeName, ProjectFileConstants.Include, StringComparison.OrdinalIgnoreCase) == 0) + { + return item.EvaluatedInclude; + } + + // Build Action is the type, not a property, so intercept this one as well + if(String.Compare(attributeName, ProjectFileConstants.BuildAction, StringComparison.OrdinalIgnoreCase) == 0) + { + return item.ItemType; + } + + return item.GetMetadataValue(attributeName); + } + + /// + /// Get the value of an attribute on a project element + /// + /// Name of the attribute to get the value for + /// Value of the attribute + public string GetMetadata(string attributeName) + { + if(this.IsVirtual) + { + // For virtual items, use our virtual property collection + if(!virtualProperties.ContainsKey(attributeName)) + return String.Empty; + return virtualProperties[attributeName]; + } + + // cannot ask MSBuild for Include, so intercept it and return the corresponding property + if(String.Compare(attributeName, ProjectFileConstants.Include, StringComparison.OrdinalIgnoreCase) == 0) + return item.EvaluatedInclude; + + // Build Action is the type, not a property, so intercept this one as well + if(String.Compare(attributeName, ProjectFileConstants.BuildAction, StringComparison.OrdinalIgnoreCase) == 0) + return item.ItemType; + + return item.GetMetadataValue(attributeName); + } + + /// + /// Gets the attribute and throws the handed exception if the exception if the attribute is empty or null. + /// + /// The name of the attribute to get. + /// The exception to be thrown if not found or empty. + /// The attribute if found + /// The method will throw an Exception and neglect the passed in exception if the attribute is deleted + public string GetMetadataAndThrow(string attributeName, Exception exception) + { + Debug.Assert(!String.IsNullOrEmpty(attributeName), "Cannot retrieve an attribute for a null or empty attribute name"); + string attribute = GetMetadata(attributeName); + + if(String.IsNullOrEmpty(attributeName) && exception != null) + { + if(String.IsNullOrEmpty(exception.Message)) + { + Debug.Assert(!String.IsNullOrEmpty(this.itemProject.BaseURI.AbsoluteUrl), "Cannot retrieve an attribute for a project that does not have a name"); + string message = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.AttributeLoad, CultureInfo.CurrentUICulture), attributeName, this.itemProject.BaseURI.AbsoluteUrl); + throw new Exception(message, exception); + } + throw exception; + } + + return attribute; + } + + + public void Rename(string newPath) + { + string escapedPath = Microsoft.Build.Evaluation.ProjectCollection.Escape(newPath); + if(this.IsVirtual) + { + virtualProperties[ProjectFileConstants.Include] = escapedPath; + return; + } + + item.Rename(escapedPath); + this.RefreshProperties(); + } + + + /// + /// Reevaluate all properties for the current item + /// This should be call if you believe the property for this item + /// may have changed since it was created/refreshed, or global properties + /// this items depends on have changed. + /// Be aware that there is a perf cost in calling this function. + /// + public void RefreshProperties() + { + if(this.IsVirtual) + return; + + itemProject.BuildProject.ReevaluateIfNecessary(); + + IEnumerable items = itemProject.BuildProject.GetItems(item.ItemType); + foreach (ProjectItem projectItem in items) + { + if(projectItem!= null && projectItem.UnevaluatedInclude.Equals(item.UnevaluatedInclude)) + { + item = projectItem; + return; + } + } + } + + /// + /// Return an absolute path for the passed in element. + /// If the element is already an absolute path, it is returned. + /// Otherwise, it is unrelativized using the project directory + /// as the base. + /// Note that any ".." in the paths will be resolved. + /// + /// For non-file system based project, it may make sense to override. + /// + /// FullPath + public string GetFullPathForElement() + { + string path = this.GetMetadata(ProjectFileConstants.Include); + if(!Path.IsPathRooted(path)) + path = Path.Combine(this.itemProject.ProjectFolder, path); + + // If any part of the path used relative paths, resolve this now + path = Path.GetFullPath(path); + return path; + } + + #endregion + + #region helper methods + /// + /// Has the item been deleted + /// + private bool HasItemBeenDeleted() + { + return (this.deleted || this.item == null); + } + #endregion + + #region overridden from System.Object + public static bool operator ==(ProjectElement element1, ProjectElement element2) + { + // Do they reference the same element? + if(Object.ReferenceEquals(element1, element2)) + return true; + + // Verify that they are not null (cast to object first to avoid stack overflow) + if(element1 as object == null || element2 as object == null) + { + return false; + } + + Debug.Assert(!element1.IsVirtual || !element2.IsVirtual, "Cannot compare virtual nodes"); + + // Cannot compare vitual items. + if(element1.IsVirtual || element2.IsVirtual) + { + return false; + } + + // Do they reference the same project? + if(!element1.itemProject.Equals(element2.itemProject)) + return false; + + // Do they have the same include? + string include1 = element1.GetMetadata(ProjectFileConstants.Include); + string include2 = element2.GetMetadata(ProjectFileConstants.Include); + + // Unfortunately the checking for nulls have to be done again, since neither String.Equals nor String.Compare can handle nulls. + // Virtual folders should not be handled here. + if(include1 == null || include2 == null) + { + return false; + } + + return String.Equals(include1, include2, StringComparison.CurrentCultureIgnoreCase); + } + + + public static bool operator !=(ProjectElement element1, ProjectElement element2) + { + return !(element1 == element2); + } + + + public override bool Equals(object obj) + { + ProjectElement element2 = obj as ProjectElement; + if(element2 == null) + return false; + + return this == element2; + } + + + public override int GetHashCode() + { + return base.GetHashCode(); + } + #endregion + + + + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectFactory.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectFactory.cs new file mode 100644 index 0000000000..9d1d0baa8c --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectFactory.cs @@ -0,0 +1,211 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using System.Windows.Forms; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using MSBuild = Microsoft.Build.Evaluation; +using MSBuildExecution = Microsoft.Build.Execution; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Creates projects within the solution + /// + [CLSCompliant(false)] + public abstract class ProjectFactory : Microsoft.VisualStudio.Shell.Flavor.FlavoredProjectFactoryBase + { + #region fields + private Microsoft.VisualStudio.Shell.Package package; + private System.IServiceProvider site; + + /// + /// The msbuild engine that we are going to use. + /// + private MSBuild.ProjectCollection buildEngine; + + /// + /// The msbuild project for the project file. + /// + private MSBuild.Project buildProject; + #endregion + + #region properties + protected Microsoft.VisualStudio.Shell.Package Package + { + get + { + return this.package; + } + } + + protected System.IServiceProvider Site + { + get + { + return this.site; + } + } + + /// + /// The msbuild engine that we are going to use. + /// + protected MSBuild.ProjectCollection BuildEngine + { + get + { + return this.buildEngine; + } + } + + /// + /// The msbuild project for the temporary project file. + /// + protected MSBuild.Project BuildProject + { + get + { + return this.buildProject; + } + set + { + this.buildProject = value; + } + } + #endregion + + #region ctor + protected ProjectFactory(Microsoft.VisualStudio.Shell.Package package) + { + this.package = package; + this.site = package; + + // Please be aware that this methods needs that ServiceProvider is valid, thus the ordering of calls in the ctor matters. + this.buildEngine = Utilities.InitializeMsBuildEngine(this.buildEngine, this.site); + } + #endregion + + #region abstract methods + protected abstract ProjectNode CreateProject(); + #endregion + + #region overriden methods + /// + /// Rather than directly creating the project, ask VS to initate the process of + /// creating an aggregated project in case we are flavored. We will be called + /// on the IVsAggregatableProjectFactory to do the real project creation. + /// + /// Project file + /// Path of the project + /// Project Name + /// Creation flags + /// Guid of the project + /// Project that end up being created by this method + /// Was the project creation canceled + protected override void CreateProject(string fileName, string location, string name, uint flags, ref Guid projectGuid, out IntPtr project, out int canceled) + { + project = IntPtr.Zero; + canceled = 0; + + // Get the list of GUIDs from the project/template + string guidsList = this.ProjectTypeGuids(fileName); + + // Launch the aggregate creation process (we should be called back on our IVsAggregatableProjectFactoryCorrected implementation) + IVsCreateAggregateProject aggregateProjectFactory = (IVsCreateAggregateProject)this.Site.GetService(typeof(SVsCreateAggregateProject)); + int hr = aggregateProjectFactory.CreateAggregateProject(guidsList, fileName, location, name, flags, ref projectGuid, out project); + if(hr == VSConstants.E_ABORT) + canceled = 1; + ErrorHandler.ThrowOnFailure(hr); + + // This needs to be done after the aggregation is completed (to avoid creating a non-aggregated CCW) and as a result we have to go through the interface + IProjectEventsProvider eventsProvider = (IProjectEventsProvider)Marshal.GetTypedObjectForIUnknown(project, typeof(IProjectEventsProvider)); + eventsProvider.ProjectEventsProvider = this.GetProjectEventsProvider(); + + this.buildProject = null; + } + + + /// + /// Instantiate the project class, but do not proceed with the + /// initialization just yet. + /// Delegate to CreateProject implemented by the derived class. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope", + Justification="The global property handles is instantiated here and used in the project node that will Dispose it")] + protected override object PreCreateForOuter(IntPtr outerProjectIUnknown) + { + Debug.Assert(this.buildProject != null, "The build project should have been initialized before calling PreCreateForOuter."); + + // Please be very carefull what is initialized here on the ProjectNode. Normally this should only instantiate and return a project node. + // The reason why one should very carefully add state to the project node here is that at this point the aggregation has not yet been created and anything that would cause a CCW for the project to be created would cause the aggregation to fail + // Our reasoning is that there is no other place where state on the project node can be set that is known by the Factory and has to execute before the Load method. + ProjectNode node = this.CreateProject(); + Debug.Assert(node != null, "The project failed to be created"); + node.BuildEngine = this.buildEngine; + node.BuildProject = this.buildProject; + node.Package = this.package as ProjectPackage; + return node; + } + + /// + /// Retrives the list of project guids from the project file. + /// If you don't want your project to be flavorable, override + /// to only return your project factory Guid: + /// return this.GetType().GUID.ToString("B"); + /// + /// Project file to look into to find the Guid list + /// List of semi-colon separated GUIDs + protected override string ProjectTypeGuids(string file) + { + // Load the project so we can extract the list of GUIDs + + this.buildProject = Utilities.ReinitializeMsBuildProject(this.buildEngine, file, this.buildProject); + + // Retrieve the list of GUIDs, if it is not specify, make it our GUID + string guids = buildProject.GetPropertyValue(ProjectFileConstants.ProjectTypeGuids); + if(String.IsNullOrEmpty(guids)) + guids = this.GetType().GUID.ToString("B"); + + return guids; + } + #endregion + + #region helpers + private IProjectEvents GetProjectEventsProvider() + { + ProjectPackage projectPackage = this.package as ProjectPackage; + Debug.Assert(projectPackage != null, "Package not inherited from framework"); + if(projectPackage != null) + { + foreach(SolutionListener listener in projectPackage.SolutionListeners) + { + IProjectEvents projectEvents = listener as IProjectEvents; + if(projectEvents != null) + { + return projectEvents; + } + } + } + + return null; + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectFileConstants.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectFileConstants.cs new file mode 100644 index 0000000000..2fbaec230f --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectFileConstants.cs @@ -0,0 +1,137 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Defines the constant strings for various msbuild targets + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")] + public static class MsBuildTarget + { + public const string ResolveProjectReferences = "ResolveProjectReferences"; + public const string ResolveAssemblyReferences = "ResolveAssemblyReferences"; + public const string ResolveComReferences = "ResolveComReferences"; + public const string Build = "Build"; + public const string Rebuild = "ReBuild"; + public const string Clean = "Clean"; + } + + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")] + public static class MsBuildGeneratedItemType + { + public const string ReferenceCopyLocalPaths = "ReferenceCopyLocalPaths"; + public const string ComReferenceWrappers = "ComReferenceWrappers"; + } + + /// + /// Defines the constant strings used with project files. + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "COM")] + public static class ProjectFileConstants + { + public const string Include = "Include"; + public const string Name = "Name"; + public const string HintPath = "HintPath"; + public const string AssemblyName = "AssemblyName"; + public const string FinalOutputPath = "FinalOutputPath"; + public const string Project = "Project"; + public const string LinkedIntoProjectAt = "LinkedIntoProjectAt"; + public const string TypeGuid = "TypeGuid"; + public const string InstanceGuid = "InstanceGuid"; + public const string Private = "Private"; + public const string ProjectReference = "ProjectReference"; + public const string Reference = "Reference"; + public const string WebReference = "WebReference"; + public const string WebReferenceFolder = "WebReferenceFolder"; + public const string Folder = "Folder"; + public const string Content = "Content"; + public const string EmbeddedResource = "EmbeddedResource"; + public const string RootNamespace = "RootNamespace"; + public const string OutputType = "OutputType"; + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "SubType")] + public const string SubType = "SubType"; + public const string DependentUpon = "DependentUpon"; + public const string Compile = "Compile"; + public const string ReferencePath = "ReferencePath"; + public const string ResolvedProjectReferencePaths = "ResolvedProjectReferencePaths"; + public const string Configuration = "Configuration"; + public const string Platform = "Platform"; + public const string AvailablePlatforms = "AvailablePlatforms"; + public const string BuildVerbosity = "BuildVerbosity"; + public const string Template = "Template"; + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "SubProject")] + public const string SubProject = "SubProject"; + public const string BuildAction = "BuildAction"; + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "COM")] + public const string COMReference = "COMReference"; + public const string Guid = "Guid"; + public const string VersionMajor = "VersionMajor"; + public const string VersionMinor = "VersionMinor"; + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Lcid")] + public const string Lcid = "Lcid"; + public const string Isolated = "Isolated"; + public const string WrapperTool = "WrapperTool"; + public const string BuildingInsideVisualStudio = "BuildingInsideVisualStudio"; + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")] + public const string SccProjectName = "SccProjectName"; + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")] + public const string SccLocalPath = "SccLocalPath"; + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")] + public const string SccAuxPath = "SccAuxPath"; + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")] + public const string SccProvider = "SccProvider"; + public const string ProjectGuid = "ProjectGuid"; + public const string ProjectTypeGuids = "ProjectTypeGuids"; + public const string Generator = "Generator"; + public const string CustomToolNamespace = "CustomToolNamespace"; + public const string FlavorProperties = "FlavorProperties"; + public const string VisualStudio = "VisualStudio"; + public const string User = "User"; + } + + public static class ProjectFileAttributeValue + { + public const string Code = "Code"; + public const string Form = "Form"; + public const string Component = "Component"; + public const string Designer = "Designer"; + public const string UserControl = "UserControl"; + } + + internal static class ProjectFileValues + { + internal const string AnyCPU = "AnyCPU"; + } + + public enum WrapperToolAttributeValue + { + Primary, + TlbImp + } + + /// + /// A set of constants that specify the default sort order for different types of hierarchy nodes. + /// + public static class DefaultSortOrderNode + { + public const int HierarchyNode = 1000; + public const int FolderNode = 500; + public const int NestedProjectNode = 200; + public const int ReferenceContainerNode = 300; + } + +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectNode.CopyPaste.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectNode.CopyPaste.cs new file mode 100644 index 0000000000..bb71bdc30b --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectNode.CopyPaste.cs @@ -0,0 +1,1170 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.Permissions; +using System.Text; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using IOleDataObject = Microsoft.VisualStudio.OLE.Interop.IDataObject; +using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Manages the CopyPaste and Drag and Drop scenarios for a Project. + /// + /// This is a partial class. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling")] + public partial class ProjectNode : IVsUIHierWinClipboardHelperEvents + { + #region fields + private uint copyPasteCookie; + private DropDataType dropDataType; + #endregion + + #region override of IVsHierarchyDropDataTarget methods + /// + /// Called as soon as the mouse drags an item over a new hierarchy or hierarchy window + /// + /// reference to interface IDataObject of the item being dragged + /// Current state of the keyboard and the mouse modifier keys. See docs for a list of possible values + /// Item identifier for the item currently being dragged + /// On entry, a pointer to the current DropEffect. On return, must contain the new valid DropEffect + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public override int DragEnter(IOleDataObject pDataObject, uint grfKeyState, uint itemid, ref uint pdwEffect) + { + pdwEffect = (uint)DropEffect.None; + + // changed from MPFProj: + // http://mpfproj10.codeplex.com/WorkItem/View.aspx?WorkItemId=8145 + /* + if(this.SourceDraggedOrCutOrCopied) + { + return VSConstants.S_OK; + }*/ + + this.dropDataType = QueryDropDataType(pDataObject); + if(this.dropDataType != DropDataType.None) + { + pdwEffect = (uint)this.QueryDropEffect(this.dropDataType, grfKeyState); + } + + return VSConstants.S_OK; + } + + /// + /// Called when one or more items are dragged out of the hierarchy or hierarchy window, or when the drag-and-drop operation is cancelled or completed. + /// + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public override int DragLeave() + { + this.dropDataType = DropDataType.None; + return VSConstants.S_OK; + } + + /// + /// Called when one or more items are dragged over the target hierarchy or hierarchy window. + /// + /// Current state of the keyboard keys and the mouse modifier buttons. See + /// Item identifier of the drop data target over which the item is being dragged + /// On entry, reference to the value of the pdwEffect parameter of the IVsHierarchy object, identifying all effects that the hierarchy supports. + /// On return, the pdwEffect parameter must contain one of the effect flags that indicate the result of the drop operation. For a list of pwdEffects values, see + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public override int DragOver(uint grfKeyState, uint itemid, ref uint pdwEffect) + { + pdwEffect = (uint)DropEffect.None; + + // Dragging items to a project that is being debugged is not supported + // (see VSWhidbey 144785) + DBGMODE dbgMode = VsShellUtilities.GetDebugMode(this.Site) & ~DBGMODE.DBGMODE_EncMask; + if(dbgMode == DBGMODE.DBGMODE_Run || dbgMode == DBGMODE.DBGMODE_Break) + { + return VSConstants.S_OK; + } + + if(this.isClosed || this.site == null) + { + return VSConstants.E_UNEXPECTED; + } + + // We should also analyze if the node being dragged over can accept the drop. + if(!this.CanTargetNodeAcceptDrop(itemid)) + { + return VSConstants.E_NOTIMPL; + } + + if(this.dropDataType != DropDataType.None) + { + pdwEffect = (uint)this.QueryDropEffect(this.dropDataType, grfKeyState); + } + + return VSConstants.S_OK; + } + + /// + /// Called when one or more items are dropped into the target hierarchy or hierarchy window when the mouse button is released. + /// + /// Reference to the IDataObject interface on the item being dragged. This data object contains the data being transferred in the drag-and-drop operation. + /// If the drop occurs, then this data object (item) is incorporated into the target hierarchy or hierarchy window. + /// Current state of the keyboard and the mouse modifier keys. See + /// Item identifier of the drop data target over which the item is being dragged + /// Visual effects associated with the drag-and drop-operation, such as a cursor, bitmap, and so on. + /// The value of dwEffects passed to the source object via the OnDropNotify method is the value of pdwEffects returned by the Drop method + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public override int Drop(IOleDataObject pDataObject, uint grfKeyState, uint itemid, ref uint pdwEffect) + { + if(pDataObject == null) + { + return VSConstants.E_INVALIDARG; + } + + pdwEffect = (uint)DropEffect.None; + + // Get the node that is being dragged over and ask it which node should handle this call + HierarchyNode targetNode = NodeFromItemId(itemid); + if(targetNode != null) + { + targetNode = targetNode.GetDragTargetHandlerNode(); + } + else + { + // There is no target node. The drop can not be completed. + return VSConstants.S_FALSE; + } + + int returnValue; + try + { + DropDataType dropDataType = DropDataType.None; + dropDataType = ProcessSelectionDataObject(pDataObject, targetNode); + pdwEffect = (uint)this.QueryDropEffect(dropDataType, grfKeyState); + + // If it is a drop from windows and we get any kind of error we return S_FALSE and dropeffect none. This + // prevents bogus messages from the shell from being displayed + returnValue = (dropDataType != DropDataType.Shell) ? VSConstants.E_FAIL : VSConstants.S_OK; + } + catch(System.IO.FileNotFoundException e) + { + Trace.WriteLine("Exception : " + e.Message); + + if(!Utilities.IsInAutomationFunction(this.Site)) + { + string message = e.Message; + string title = string.Empty; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton); + } + + returnValue = VSConstants.E_FAIL; + } + + return returnValue; + } + #endregion + + #region override of IVsHierarchyDropDataSource2 methods + /// + /// Returns information about one or more of the items being dragged + /// + /// Pointer to a DWORD value describing the effects displayed while the item is being dragged, + /// such as cursor icons that change during the drag-and-drop operation. + /// For example, if the item is dragged over an invalid target point + /// (such as the item's original location), the cursor icon changes to a circle with a line through it. + /// Similarly, if the item is dragged over a valid target point, the cursor icon changes to a file or folder. + /// Pointer to the IDataObject interface on the item being dragged. + /// This data object contains the data being transferred in the drag-and-drop operation. + /// If the drop occurs, then this data object (item) is incorporated into the target hierarchy or hierarchy window. + /// Pointer to the IDropSource interface of the item being dragged. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public override int GetDropInfo(out uint pdwOKEffects, out IOleDataObject ppDataObject, out IDropSource ppDropSource) + { + //init out params + pdwOKEffects = (uint)DropEffect.None; + ppDataObject = null; + ppDropSource = null; + + IOleDataObject dataObject = PackageSelectionDataObject(false); + if(dataObject == null) + { + return VSConstants.E_NOTIMPL; + } + + this.SourceDraggedOrCutOrCopied = true; + + pdwOKEffects = (uint)(DropEffect.Move | DropEffect.Copy); + + ppDataObject = dataObject; + return VSConstants.S_OK; + } + + /// + /// Notifies clients that the dragged item was dropped. + /// + /// If true, then the dragged item was dropped on the target. If false, then the drop did not occur. + /// Visual effects associated with the drag-and-drop operation, such as cursors, bitmaps, and so on. + /// The value of dwEffects passed to the source object via OnDropNotify method is the value of pdwEffects returned by Drop method. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public override int OnDropNotify(int fDropped, uint dwEffects) + { + if(!this.SourceDraggedOrCutOrCopied) + { + return VSConstants.S_FALSE; + } + + this.CleanupSelectionDataObject(fDropped != 0, false, dwEffects == (uint)DropEffect.Move); + + this.SourceDraggedOrCutOrCopied = false; + + return VSConstants.S_OK; + } + + /// + /// Allows the drag source to prompt to save unsaved items being dropped. + /// Notifies the source hierarchy that information dragged from it is about to be dropped on a target. + /// This method is called immediately after the mouse button is released on a drop. + /// + /// Reference to the IDataObject interface on the item being dragged. + /// This data object contains the data being transferred in the drag-and-drop operation. + /// If the drop occurs, then this data object (item) is incorporated into the hierarchy window of the new hierarchy. + /// Current state of the keyboard and the mouse modifier keys. + /// If true, then the drop is cancelled by the source hierarchy. If false, then the drop can continue. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public override int OnBeforeDropNotify(IOleDataObject o, uint dwEffect, out int fCancelDrop) + { + // If there is nothing to be dropped just return that drop should be cancelled. + if(this.ItemsDraggedOrCutOrCopied == null) + { + fCancelDrop = 1; + return VSConstants.S_OK; + } + + fCancelDrop = 0; + bool dirty = false; + foreach(HierarchyNode node in this.ItemsDraggedOrCutOrCopied) + { + bool isDirty, isOpen, isOpenedByUs; + uint docCookie; + IVsPersistDocData ppIVsPersistDocData; + DocumentManager manager = node.GetDocumentManager(); + if(manager != null) + { + manager.GetDocInfo(out isOpen, out isDirty, out isOpenedByUs, out docCookie, out ppIVsPersistDocData); + if(isDirty && isOpenedByUs) + { + dirty = true; + break; + } + } + } + + // if there are no dirty docs we are ok to proceed + if(!dirty) + { + return VSConstants.S_OK; + } + + // Prompt to save if there are dirty docs + string message = SR.GetString(SR.SaveModifiedDocuments, CultureInfo.CurrentUICulture); + string title = string.Empty; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_WARNING; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_YESNOCANCEL; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + int result = VsShellUtilities.ShowMessageBox(Site, title, message, icon, buttons, defaultButton); + switch(result) + { + case NativeMethods.IDYES: + break; + + case NativeMethods.IDNO: + return VSConstants.S_OK; + + case NativeMethods.IDCANCEL: goto default; + + default: + fCancelDrop = 1; + return VSConstants.S_OK; + } + + // Save all dirty documents + foreach(HierarchyNode node in this.ItemsDraggedOrCutOrCopied) + { + DocumentManager manager = node.GetDocumentManager(); + if(manager != null) + { + manager.Save(true); + } + } + + return VSConstants.S_OK; + } + + #endregion + + #region IVsUIHierWinClipboardHelperEvents Members + /// + /// Called after your cut/copied items has been pasted + /// + ///If true, then the IDataObject has been successfully pasted into a target hierarchy. + /// If false, then the cut or copy operation was cancelled. + /// Visual effects associated with the drag and drop operation, such as cursors, bitmaps, and so on. + /// These should be the same visual effects used in OnDropNotify + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int OnPaste(int wasCut, uint dropEffect) + { + if(!this.SourceDraggedOrCutOrCopied) + { + return VSConstants.S_FALSE; + } + + if(dropEffect == (uint)DropEffect.None) + { + return OnClear(wasCut); + } + + this.CleanupSelectionDataObject(false, wasCut != 0, dropEffect == (uint)DropEffect.Move); + this.SourceDraggedOrCutOrCopied = false; + return VSConstants.S_OK; + } + + /// + /// Called when your cut/copied operation is canceled + /// + /// This flag informs the source that the Cut method was called (true), + /// rather than Copy (false), so the source knows whether to "un-cut-highlight" the items that were cut. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int OnClear(int wasCut) + { + if(!this.SourceDraggedOrCutOrCopied) + { + return VSConstants.S_FALSE; + } + + this.CleanupSelectionDataObject(false, wasCut != 0, false, true); + this.SourceDraggedOrCutOrCopied = false; + return VSConstants.S_OK; + } + #endregion + + #region virtual methods + /// + /// Determines if a node can accept drop opertaion. + /// + /// The id of the node. + /// true if the node acceots drag operation. + protected internal virtual bool CanTargetNodeAcceptDrop(uint itemId) + { + HierarchyNode targetNode = NodeFromItemId(itemId); + if(targetNode is ReferenceContainerNode || targetNode is ReferenceNode) + { + return false; + } + else + { + return true; + } + } + + /// + /// Returns a dataobject from selected nodes + /// + /// boolean that defines if the selected items must be cut + /// data object for selected items + internal virtual DataObject PackageSelectionDataObject(bool cutHighlightItems) + { + this.CleanupSelectionDataObject(false, false, false); + StringBuilder sb = new StringBuilder(); + + DataObject dataObject = null; + + try + { + IList selectedNodes = this.GetSelectedNodes(); + if(selectedNodes != null) + { + this.InstantiateItemsDraggedOrCutOrCopiedList(); + + StringBuilder selectionContent = null; + + // If there is a selection package the data + if(selectedNodes.Count > 1) + { + foreach(HierarchyNode node in selectedNodes) + { + selectionContent = node.PrepareSelectedNodesForClipBoard(); + if(selectionContent != null) + { + sb.Append(selectionContent); + } + } + } + else if(selectedNodes.Count == 1) + { + HierarchyNode selectedNode = selectedNodes[0]; + selectionContent = selectedNode.PrepareSelectedNodesForClipBoard(); + if(selectionContent != null) + { + sb.Append(selectionContent); + } + } + } + + // Add the project items first. + IntPtr ptrToItems = this.PackageSelectionData(sb, false); + if(ptrToItems == IntPtr.Zero) + { + return null; + } + + FORMATETC fmt = DragDropHelper.CreateFormatEtc(DragDropHelper.CF_VSSTGPROJECTITEMS); + dataObject = new DataObject(); + dataObject.SetData(fmt, ptrToItems); + + // Now add the project path that sourced data. We just write the project file path. + IntPtr ptrToProjectPath = this.PackageSelectionData(new StringBuilder(this.GetMkDocument()), true); + + if(ptrToProjectPath != IntPtr.Zero) + { + dataObject.SetData(DragDropHelper.CreateFormatEtc(DragDropHelper.CF_VSPROJECTCLIPDESCRIPTOR), ptrToProjectPath); + } + + if(cutHighlightItems) + { + bool first = true; + IVsUIHierarchyWindow w = UIHierarchyUtilities.GetUIHierarchyWindow(this.site, HierarchyNode.SolutionExplorer); + + foreach(HierarchyNode node in this.ItemsDraggedOrCutOrCopied) + { + ErrorHandler.ThrowOnFailure(w.ExpandItem((IVsUIHierarchy)this, node.ID, first ? EXPANDFLAGS.EXPF_CutHighlightItem : EXPANDFLAGS.EXPF_AddCutHighlightItem)); + first = false; + } + } + } + catch(COMException e) + { + Trace.WriteLine("Exception : " + e.Message); + + dataObject = null; + } + + return dataObject; + } + + + /// + /// This is used to recursively add a folder from an other project. + /// Note that while we copy the folder content completely, we only + /// add to the project items which are part of the source project. + /// + /// Project reference (from data object) using the format: {Guid}|project|folderPath + /// Node to add the new folder to + protected internal virtual void AddFolderFromOtherProject(string folderToAdd, HierarchyNode targetNode) + { + if(String.IsNullOrEmpty(folderToAdd)) + throw new ArgumentNullException("folderToAdd"); + if(targetNode == null) + throw new ArgumentNullException("targetNode"); + + // Split the reference in its 3 parts + int index1 = Guid.Empty.ToString("B").Length; + if(index1 + 1 >= folderToAdd.Length) + throw new ArgumentOutOfRangeException("folderToAdd"); + + // Get the Guid + string guidString = folderToAdd.Substring(1, index1 - 2); + Guid projectInstanceGuid = new Guid(guidString); + + // Get the project path + int index2 = folderToAdd.IndexOf('|', index1 + 1); + if(index2 < 0 || index2 + 1 >= folderToAdd.Length) + throw new ArgumentOutOfRangeException("folderToAdd"); + + // Finally get the source path + string folder = folderToAdd.Substring(index2 + 1); + + // Get the target path + string folderName = Path.GetFileName(Path.GetDirectoryName(folder)); + string targetPath = Path.Combine(GetBaseDirectoryForAddingFiles(targetNode), folderName); + + // Recursively copy the directory to the new location + Utilities.RecursivelyCopyDirectory(folder, targetPath); + + // Retrieve the project from which the items are being copied + IVsHierarchy sourceHierarchy; + IVsSolution solution = (IVsSolution)GetService(typeof(SVsSolution)); + ErrorHandler.ThrowOnFailure(solution.GetProjectOfGuid(ref projectInstanceGuid, out sourceHierarchy)); + + // Then retrieve the item ID of the item to copy + uint itemID = VSConstants.VSITEMID_ROOT; + ErrorHandler.ThrowOnFailure(sourceHierarchy.ParseCanonicalName(folder, out itemID)); + + // Ensure we don't end up in an endless recursion + if(Utilities.IsSameComObject(this, sourceHierarchy)) + { + HierarchyNode cursorNode = targetNode; + while(cursorNode != null) + { + if(String.Compare(folder, cursorNode.GetMkDocument(), StringComparison.OrdinalIgnoreCase) == 0) + throw new Exception(); + cursorNode = cursorNode.Parent; + } + } + + // Now walk the source project hierarchy to see which node needs to be added. + WalkSourceProjectAndAdd(sourceHierarchy, itemID, targetNode, false); + } + + /// + /// Recursive method that walk a hierarchy and add items it find to our project. + /// Note that this is meant as an helper to the Copy&Paste/Drag&Drop functionality. + /// + /// Hierarchy to walk + /// Item ID where to start walking the hierarchy + /// Node to start adding to + /// Typically false on first call and true after that + protected virtual void WalkSourceProjectAndAdd(IVsHierarchy sourceHierarchy, uint itemId, HierarchyNode targetNode, bool addSiblings) + { + if (sourceHierarchy == null) + { + throw new ArgumentNullException("sourceHierarchy"); + } + + // Before we start the walk, add the current node + object variant = null; + HierarchyNode newNode = targetNode; + if(itemId != VSConstants.VSITEMID_NIL) + { + // Calculate the corresponding path in our project + string source; + ErrorHandler.ThrowOnFailure(((IVsProject)sourceHierarchy).GetMkDocument(itemId, out source)); + string name = Path.GetFileName(source.TrimEnd(new char[] { '/', '\\' })); + string targetPath = Path.Combine(GetBaseDirectoryForAddingFiles(targetNode), name); + + // See if this is a linked item (file can be linked, not folders) + ErrorHandler.ThrowOnFailure(sourceHierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_BrowseObject, out variant), VSConstants.E_NOTIMPL); + VSLangProj.FileProperties fileProperties = variant as VSLangProj.FileProperties; + if(fileProperties != null && fileProperties.IsLink) + { + // Since we don't support linked item, we make a copy of the file into our storage where it would have been linked + File.Copy(source, targetPath, true); + } + + newNode = AddNodeIfTargetExistInStorage(targetNode, name, targetPath); + + + // Start with child nodes (depth first) + variant = null; + ErrorHandler.ThrowOnFailure(sourceHierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_FirstVisibleChild, out variant)); + uint currentItemID = (uint)(int)variant; + WalkSourceProjectAndAdd(sourceHierarchy, currentItemID, newNode, true); + + if(addSiblings) + { + // Then look at siblings + currentItemID = itemId; + while(currentItemID != VSConstants.VSITEMID_NIL) + { + variant = null; + ErrorHandler.ThrowOnFailure(sourceHierarchy.GetProperty(itemId, (int)__VSHPROPID.VSHPROPID_NextVisibleSibling, out variant)); + currentItemID = (uint)(int)variant; + WalkSourceProjectAndAdd(sourceHierarchy, currentItemID, targetNode, true); + } + } + } + } + + /// + /// Add an existing item (file/folder) to the project if it already exist in our storage. + /// + /// Node to that this item to + /// Name of the item being added + /// Path of the item being added + /// Node that was added + protected virtual HierarchyNode AddNodeIfTargetExistInStorage(HierarchyNode parentNode, string name, string targetPath) + { + if (parentNode == null) + { + return null; + } + + HierarchyNode newNode = parentNode; + // If the file/directory exist, add a node for it + if(File.Exists(targetPath)) + { + VSADDRESULT[] result = new VSADDRESULT[1]; + ErrorHandler.ThrowOnFailure(this.AddItem(parentNode.ID, VSADDITEMOPERATION.VSADDITEMOP_OPENFILE, name, 1, new string[] { targetPath }, IntPtr.Zero, result)); + if(result[0] != VSADDRESULT.ADDRESULT_Success) + throw new Exception(); + newNode = this.FindChild(targetPath); + if(newNode == null) + throw new Exception(); + } + else if(Directory.Exists(targetPath)) + { + newNode = this.CreateFolderNodes(targetPath); + } + return newNode; + } + #endregion + + #region non-virtual methods + /// + /// Handle the Cut operation to the clipboard + /// + protected internal override int CutToClipboard() + { + int returnValue = (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + try + { + this.RegisterClipboardNotifications(true); + + // Create our data object and change the selection to show item(s) being cut + IOleDataObject dataObject = this.PackageSelectionDataObject(true); + if(dataObject != null) + { + this.SourceDraggedOrCutOrCopied = true; + + // Add our cut item(s) to the clipboard + ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.OleSetClipboard(dataObject)); + + // Inform VS (UiHierarchyWindow) of the cut + IVsUIHierWinClipboardHelper clipboardHelper = (IVsUIHierWinClipboardHelper)GetService(typeof(SVsUIHierWinClipboardHelper)); + if(clipboardHelper == null) + { + return VSConstants.E_FAIL; + } + + returnValue = ErrorHandler.ThrowOnFailure(clipboardHelper.Cut(dataObject)); + } + } + catch(COMException e) + { + Trace.WriteLine("Exception : " + e.Message); + returnValue = e.ErrorCode; + } + + return returnValue; + } + + /// + /// Handle the Copy operation to the clipboard + /// + protected internal override int CopyToClipboard() + { + int returnValue = (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + try + { + this.RegisterClipboardNotifications(true); + + // Create our data object and change the selection to show item(s) being copy + IOleDataObject dataObject = this.PackageSelectionDataObject(false); + if(dataObject != null) + { + this.SourceDraggedOrCutOrCopied = true; + + // Add our copy item(s) to the clipboard + ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.OleSetClipboard(dataObject)); + + // Inform VS (UiHierarchyWindow) of the copy + IVsUIHierWinClipboardHelper clipboardHelper = (IVsUIHierWinClipboardHelper)GetService(typeof(SVsUIHierWinClipboardHelper)); + if(clipboardHelper == null) + { + return VSConstants.E_FAIL; + } + returnValue = ErrorHandler.ThrowOnFailure(clipboardHelper.Copy(dataObject)); + } + } + catch(COMException e) + { + Trace.WriteLine("Exception : " + e.Message); + returnValue = e.ErrorCode; + } + catch(ArgumentException e) + { + Trace.WriteLine("Exception : " + e.Message); + returnValue = Marshal.GetHRForException(e); + } + + return returnValue; + } + + /// + /// Handle the Paste operation to a targetNode + /// + protected internal override int PasteFromClipboard(HierarchyNode targetNode) + { + int returnValue = (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + + if (targetNode == null) + { + return VSConstants.E_INVALIDARG; + } + + //Get the clipboardhelper service and use it after processing dataobject + IVsUIHierWinClipboardHelper clipboardHelper = (IVsUIHierWinClipboardHelper)GetService(typeof(SVsUIHierWinClipboardHelper)); + if(clipboardHelper == null) + { + return VSConstants.E_FAIL; + } + + try + { + //Get dataobject from clipboard + IOleDataObject dataObject = null; + ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.OleGetClipboard(out dataObject)); + if(dataObject == null) + { + return VSConstants.E_UNEXPECTED; + } + + DropEffect dropEffect = DropEffect.None; + DropDataType dropDataType = DropDataType.None; + try + { + dropDataType = this.ProcessSelectionDataObject(dataObject, targetNode.GetDragTargetHandlerNode()); + dropEffect = this.QueryDropEffect(dropDataType, 0); + } + catch(ExternalException e) + { + Trace.WriteLine("Exception : " + e.Message); + + // If it is a drop from windows and we get any kind of error ignore it. This + // prevents bogus messages from the shell from being displayed + if(dropDataType != DropDataType.Shell) + { + throw; + } + } + finally + { + // Inform VS (UiHierarchyWindow) of the paste + returnValue = clipboardHelper.Paste(dataObject, (uint)dropEffect); + } + } + catch(COMException e) + { + Trace.WriteLine("Exception : " + e.Message); + + returnValue = e.ErrorCode; + } + + return returnValue; + } + + /// + /// Determines if the paste command should be allowed. + /// + /// + protected internal override bool AllowPasteCommand() + { + IOleDataObject dataObject = null; + try + { + ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.OleGetClipboard(out dataObject)); + if(dataObject == null) + { + return false; + } + + // First see if this is a set of storage based items + FORMATETC format = DragDropHelper.CreateFormatEtc((ushort)DragDropHelper.CF_VSSTGPROJECTITEMS); + if(dataObject.QueryGetData(new FORMATETC[] { format }) == VSConstants.S_OK) + return true; + // Try reference based items + format = DragDropHelper.CreateFormatEtc((ushort)DragDropHelper.CF_VSREFPROJECTITEMS); + if(dataObject.QueryGetData(new FORMATETC[] { format }) == VSConstants.S_OK) + return true; + // Try windows explorer files format + format = DragDropHelper.CreateFormatEtc((ushort)NativeMethods.CF_HDROP); + return (dataObject.QueryGetData(new FORMATETC[] { format }) == VSConstants.S_OK); + } + // We catch External exceptions since it might be that it is not our data on the clipboard. + catch(ExternalException e) + { + Trace.WriteLine("Exception :" + e.Message); + return false; + } + } + + /// + /// Register/Unregister for Clipboard events for the UiHierarchyWindow (solution explorer) + /// + /// true for register, false for unregister + protected internal override void RegisterClipboardNotifications(bool register) + { + // Get the UiHierarchy window clipboard helper service + IVsUIHierWinClipboardHelper clipboardHelper = (IVsUIHierWinClipboardHelper)GetService(typeof(SVsUIHierWinClipboardHelper)); + if(clipboardHelper == null) + { + return; + } + + if(register && this.copyPasteCookie == 0) + { + // Register + ErrorHandler.ThrowOnFailure(clipboardHelper.AdviseClipboardHelperEvents(this, out this.copyPasteCookie)); + Debug.Assert(this.copyPasteCookie != 0, "AdviseClipboardHelperEvents returned an invalid cookie"); + } + else if(!register && this.copyPasteCookie != 0) + { + // Unregister + ErrorHandler.ThrowOnFailure(clipboardHelper.UnadviseClipboardHelperEvents(this.copyPasteCookie)); + this.copyPasteCookie = 0; + } + } + + /// + /// Process dataobject from Drag/Drop/Cut/Copy/Paste operation + /// + /// The targetNode is set if the method is called from a drop operation, otherwise it is null + internal DropDataType ProcessSelectionDataObject(IOleDataObject dataObject, HierarchyNode targetNode) + { + DropDataType dropDataType = DropDataType.None; + bool isWindowsFormat = false; + + // Try to get it as a directory based project. + List filesDropped = DragDropHelper.GetDroppedFiles(DragDropHelper.CF_VSSTGPROJECTITEMS, dataObject, out dropDataType); + if(filesDropped.Count == 0) + { + filesDropped = DragDropHelper.GetDroppedFiles(DragDropHelper.CF_VSREFPROJECTITEMS, dataObject, out dropDataType); + } + if(filesDropped.Count == 0) + { + filesDropped = DragDropHelper.GetDroppedFiles(NativeMethods.CF_HDROP, dataObject, out dropDataType); + isWindowsFormat = (filesDropped.Count > 0); + } + + if(dropDataType != DropDataType.None && filesDropped.Count > 0) + { + string[] filesDroppedAsArray = filesDropped.ToArray(); + + HierarchyNode node = (targetNode == null) ? this : targetNode; + + // For directory based projects the content of the clipboard is a double-NULL terminated list of Projref strings. + if(isWindowsFormat) + { + // This is the code path when source is windows explorer + VSADDRESULT[] vsaddresults = new VSADDRESULT[1]; + vsaddresults[0] = VSADDRESULT.ADDRESULT_Failure; + int addResult = AddItem(node.ID, VSADDITEMOPERATION.VSADDITEMOP_OPENFILE, null, (uint)filesDropped.Count, filesDroppedAsArray, IntPtr.Zero, vsaddresults); + if(addResult != VSConstants.S_OK && addResult != VSConstants.S_FALSE && addResult != (int)OleConstants.OLECMDERR_E_CANCELED + && vsaddresults[0] != VSADDRESULT.ADDRESULT_Success) + { + ErrorHandler.ThrowOnFailure(addResult); + } + + return dropDataType; + } + else + { + if(AddFilesFromProjectReferences(node, filesDroppedAsArray)) + { + return dropDataType; + } + } + } + + // If we reached this point then the drop data must be set to None. + // Otherwise the OnPaste will be called with a valid DropData and that would actually delete the item. + return DropDataType.None; + } + + /// + /// Get the dropdatatype from the dataobject + /// + /// The dataobject to be analysed for its format + /// dropdatatype or none if dataobject does not contain known format + internal static DropDataType QueryDropDataType(IOleDataObject pDataObject) + { + if(pDataObject == null) + { + return DropDataType.None; + } + + // known formats include File Drops (as from WindowsExplorer), + // VSProject Reference Items and VSProject Storage Items. + FORMATETC fmt = DragDropHelper.CreateFormatEtc(NativeMethods.CF_HDROP); + + if(DragDropHelper.QueryGetData(pDataObject, ref fmt) == VSConstants.S_OK) + { + return DropDataType.Shell; + } + + fmt.cfFormat = DragDropHelper.CF_VSREFPROJECTITEMS; + if(DragDropHelper.QueryGetData(pDataObject, ref fmt) == VSConstants.S_OK) + { + // Data is from a Ref-based project. + return DropDataType.VsRef; + } + + fmt.cfFormat = DragDropHelper.CF_VSSTGPROJECTITEMS; + if(DragDropHelper.QueryGetData(pDataObject, ref fmt) == VSConstants.S_OK) + { + return DropDataType.VsStg; + } + + return DropDataType.None; + } + + /// + /// Returns the drop effect. + /// + /// + /// // A directory based project should perform as follow: + /// NO MODIFIER + /// - COPY if not from current hierarchy, + /// - MOVE if from current hierarchy + /// SHIFT DRAG - MOVE + /// CTRL DRAG - COPY + /// CTRL-SHIFT DRAG - NO DROP (used for reference based projects only) + /// + internal DropEffect QueryDropEffect(DropDataType dropDataType, uint grfKeyState) + { + //Validate the dropdatatype + if((dropDataType != DropDataType.Shell) && (dropDataType != DropDataType.VsRef) && (dropDataType != DropDataType.VsStg)) + { + return DropEffect.None; + } + + // CTRL-SHIFT + if((grfKeyState & NativeMethods.MK_CONTROL) != 0 && (grfKeyState & NativeMethods.MK_SHIFT) != 0) + { + // Because we are not referenced base, we don't support link + return DropEffect.None; + } + + // CTRL + if((grfKeyState & NativeMethods.MK_CONTROL) != 0) + return DropEffect.Copy; + + // SHIFT + if((grfKeyState & NativeMethods.MK_SHIFT) != 0) + return DropEffect.Move; + + // no modifier + if(this.SourceDraggedOrCutOrCopied) + { + return DropEffect.Move; + } + else + { + return DropEffect.Copy; + } + } + + internal void CleanupSelectionDataObject(bool dropped, bool cut, bool moved) + { + this.CleanupSelectionDataObject(dropped, cut, moved, false); + } + + /// + /// After a drop or paste, will use the dwEffects + /// to determine whether we need to clean up the source nodes or not. If + /// justCleanup is set, it only does the cleanup work. + /// + internal void CleanupSelectionDataObject(bool dropped, bool cut, bool moved, bool justCleanup) + { + if(this.ItemsDraggedOrCutOrCopied == null || this.ItemsDraggedOrCutOrCopied.Count == 0) + { + return; + } + + try + { + IVsUIHierarchyWindow w = UIHierarchyUtilities.GetUIHierarchyWindow(this.site, HierarchyNode.SolutionExplorer); + foreach(HierarchyNode node in this.ItemsDraggedOrCutOrCopied) + { + if((moved && (cut || dropped) && !justCleanup)) + { + // do not close it if the doc is dirty or we do not own it + bool isDirty, isOpen, isOpenedByUs; + uint docCookie; + IVsPersistDocData ppIVsPersistDocData; + DocumentManager manager = node.GetDocumentManager(); + if(manager != null) + { + manager.GetDocInfo(out isOpen, out isDirty, out isOpenedByUs, out docCookie, out ppIVsPersistDocData); + if(isDirty || (isOpen && !isOpenedByUs)) + { + continue; + } + + // close it if opened + if(isOpen) + { + manager.Close(__FRAMECLOSE.FRAMECLOSE_NoSave); + } + } + + node.Remove(true); + } + else if(w != null) + { + ErrorHandler.ThrowOnFailure(w.ExpandItem((IVsUIHierarchy)this, node.ID, EXPANDFLAGS.EXPF_UnCutHighlightItem)); + } + } + } + finally + { + try + { + // Now delete the memory allocated by the packaging of datasources. + // If we just did a cut, or we are told to cleanup, then we need to free the data object. Otherwise, we leave it + // alone so that you can continue to paste the data in new locations. + if(moved || cut || justCleanup) + { + this.ItemsDraggedOrCutOrCopied.Clear(); + this.CleanAndFlushClipboard(); + } + } + finally + { + this.dropDataType = DropDataType.None; + } + } + } + + /// + /// Moves files from one part of our project to another. + /// + /// the targetHandler node + /// List of projectref string + /// true if succeeded + internal bool AddFilesFromProjectReferences(HierarchyNode targetNode, string[] projectReferences) + { + //Validate input + if(projectReferences == null) + { + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "projectReferences"); + } + if(targetNode == null) + { + throw new InvalidOperationException(); + } + + //Iteratively add files from projectref + foreach(string projectReference in projectReferences) + { + if(projectReference == null) + { + // bad projectref, bail out + return false; + } + if(projectReference.EndsWith("/", StringComparison.Ordinal) || projectReference.EndsWith("\\", StringComparison.Ordinal)) + { + AddFolderFromOtherProject(projectReference, targetNode); + } + else if(!AddFileToNodeFromProjectReference(projectReference, targetNode)) + { + return false; + } + } + + return true; + } + + #endregion + + #region private helper methods + /// + /// Empties all the data structures added to the clipboard and flushes the clipboard. + /// + private void CleanAndFlushClipboard() + { + IOleDataObject oleDataObject = null; + ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.OleGetClipboard(out oleDataObject)); + if(oleDataObject == null) + { + return; + } + + + string sourceProjectPath = DragDropHelper.GetSourceProjectPath(oleDataObject); + + if(!String.IsNullOrEmpty(sourceProjectPath) && NativeMethods.IsSamePath(sourceProjectPath, this.GetMkDocument())) + { + ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.OleFlushClipboard()); + int clipboardOpened = 0; + try + { + ErrorHandler.ThrowOnFailure(clipboardOpened = UnsafeNativeMethods.OpenClipboard(IntPtr.Zero)); + ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.EmptyClipboard()); + } + finally + { + if(clipboardOpened == 1) + { + ErrorHandler.ThrowOnFailure(UnsafeNativeMethods.CloseClipboard()); + } + } + } + } + + private IntPtr PackageSelectionData(StringBuilder sb, bool addEndFormatDelimiter) + { + if(sb == null || sb.ToString().Length == 0 || this.ItemsDraggedOrCutOrCopied.Count == 0) + { + return IntPtr.Zero; + } + + // Double null at end. + if(addEndFormatDelimiter) + { + if(sb.ToString()[sb.Length - 1] != '\0') + { + sb.Append('\0'); + } + } + + // We request unmanaged permission to execute the below. + new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); + + _DROPFILES df = new _DROPFILES(); + int dwSize = Marshal.SizeOf(df); + Int16 wideChar = 0; + int dwChar = Marshal.SizeOf(wideChar); + int structSize = dwSize + ((sb.Length + 1) * dwChar); + IntPtr ptr = Marshal.AllocHGlobal(structSize); + df.pFiles = dwSize; + df.fWide = 1; + IntPtr data = IntPtr.Zero; + try + { + data = UnsafeNativeMethods.GlobalLock(ptr); + Marshal.StructureToPtr(df, data, false); + IntPtr strData = new IntPtr((long)data + dwSize); + DragDropHelper.CopyStringToHGlobal(sb.ToString(), strData, structSize); + } + finally + { + if(data != IntPtr.Zero) + UnsafeNativeMethods.GlobalUnLock(data); + } + + return ptr; + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectNode.Events.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectNode.Events.cs new file mode 100644 index 0000000000..770e09ab0a --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectNode.Events.cs @@ -0,0 +1,45 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; + +namespace Microsoft.VisualStudio.Project +{ + public partial class ProjectNode + { + #region fields + private EventHandler projectPropertiesListeners; + #endregion + + #region events + public event EventHandler OnProjectPropertyChanged + { + add { projectPropertiesListeners += value; } + remove { projectPropertiesListeners -= value; } + } + #endregion + + #region methods + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1030:UseEventsWhereAppropriate")] + protected void RaiseProjectPropertyChanged(string propertyName, string oldValue, string newValue) + { + if(null != projectPropertiesListeners) + { + projectPropertiesListeners(this, new ProjectPropertyChangedArgs(propertyName, oldValue, newValue)); + } + } + #endregion + } + +} \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectNode.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectNode.cs new file mode 100644 index 0000000000..c3f2149837 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectNode.cs @@ -0,0 +1,6152 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.CodeDom.Compiler; +using System.Collections.Generic; +using System.Collections.Specialized; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Linq; +using System.Runtime.InteropServices; +using System.Xml; +using EnvDTE; +using Microsoft.Build.Evaluation; +using Microsoft.Build.Execution; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; +using IServiceProvider = System.IServiceProvider; +using MSBuild = Microsoft.Build.Evaluation; +using MSBuildConstruction = Microsoft.Build.Construction; +using MSBuildExecution = Microsoft.Build.Execution; +using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants; +using VsCommands = Microsoft.VisualStudio.VSConstants.VSStd97CmdID; +using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID; +using Microsoft.Build.BackEnd; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Manages the persistent state of the project (References, options, files, etc.) and deals with user interaction via a GUI in the form a hierarchy. + /// + [CLSCompliant(false)] + [ComVisible(true)] + public abstract partial class ProjectNode : HierarchyNode, + IVsGetCfgProvider, + IVsProject3, + IVsAggregatableProject, + IVsProjectFlavorCfgProvider, + IPersistFileFormat, + IVsProjectBuildSystem, + IVsBuildPropertyStorage, + IVsComponentUser, + IVsDependencyProvider, + IVsSccProject2, + IBuildDependencyUpdate, + IProjectEventsListener, + IProjectEventsProvider, + IReferenceContainerProvider, + IVsProjectSpecialFiles + { + #region nested types + + public enum ImageName + { + OfflineWebApp = 0, + WebReferencesFolder = 1, + OpenReferenceFolder = 2, + ReferenceFolder = 3, + Reference = 4, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SDL")] + SDLWebReference = 5, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "DISCO")] + DISCOWebReference = 6, + Folder = 7, + OpenFolder = 8, + ExcludedFolder = 9, + OpenExcludedFolder = 10, + ExcludedFile = 11, + DependentFile = 12, + MissingFile = 13, + WindowsForm = 14, + WindowsUserControl = 15, + WindowsComponent = 16, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "XML")] + XMLSchema = 17, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "XML")] + XMLFile = 18, + WebForm = 19, + WebService = 20, + WebUserControl = 21, + WebCustomUserControl = 22, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ASP")] + ASPPage = 23, + GlobalApplicationClass = 24, + WebConfig = 25, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "HTML")] + HTMLPage = 26, + StyleSheet = 27, + ScriptFile = 28, + TextFile = 29, + SettingsFile = 30, + Resources = 31, + Bitmap = 32, + Icon = 33, + Image = 34, + ImageMap = 35, + XWorld = 36, + Audio = 37, + Video = 38, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CAB")] + CAB = 39, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "JAR")] + JAR = 40, + DataEnvironment = 41, + PreviewFile = 42, + DanglingReference = 43, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "XSLT")] + XSLTFile = 44, + Cursor = 45, + AppDesignerFolder = 46, + Data = 47, + Application = 48, + DataSet = 49, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "PFX")] + PFX = 50, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SNK")] + SNK = 51, + + ImageLast = 51 + } + + /// + /// Flags for specifying which events to stop triggering. + /// + [Flags] + internal enum EventTriggering + { + TriggerAll = 0, + DoNotTriggerHierarchyEvents = 1, + DoNotTriggerTrackerEvents = 2 + } + + #endregion + + #region constants + /// + /// The user file extension. + /// + internal const string PerUserFileExtension = ".user"; + #endregion + + #region fields + /// + /// List of output groups names and their associated target + /// + private static KeyValuePair[] outputGroupNames = + { // Name Target (MSBuild) + new KeyValuePair("Built", "BuiltProjectOutputGroup"), + new KeyValuePair("ContentFiles", "ContentFilesProjectOutputGroup"), + new KeyValuePair("LocalizedResourceDlls", "SatelliteDllsProjectOutputGroup"), + new KeyValuePair("Documentation", "DocumentationProjectOutputGroup"), + new KeyValuePair("Symbols", "DebugSymbolsProjectOutputGroup"), + new KeyValuePair("SourceFiles", "SourceFilesProjectOutputGroup"), + new KeyValuePair("XmlSerializer", "SGenFilesOutputGroup"), + }; + + /// A project will only try to build if it can obtain a lock on this object + private volatile static object BuildLock = new object(); + + /// Maps integer ids to project item instances + private EventSinkCollection itemIdMap = new EventSinkCollection(); + + /// A service provider call back object provided by the IDE hosting the project manager + private ServiceProvider site; + + private TrackDocumentsHelper tracker; + + /// + /// This property returns the time of the last change made to this project. + /// It is not the time of the last change on the project file, but actually of + /// the in memory project settings. In other words, it is the last time that + /// SetProjectDirty was called. + /// + private DateTime lastModifiedTime; + + /// + /// MSBuild engine we are going to use + /// + private MSBuild.ProjectCollection buildEngine; + + private Microsoft.Build.Utilities.Logger buildLogger; + + private bool useProvidedLogger; + + private MSBuild.Project buildProject; + + private MSBuildExecution.ProjectInstance currentConfig; + + private ConfigProvider configProvider; + + private TaskProvider taskProvider; + + private string filename; + + private Microsoft.VisualStudio.Shell.Url baseUri; + + private bool isDirty; + + private bool isNewProject; + + private bool projectOpened; + + private bool buildIsPrepared; + + private ImageHandler imageHandler; + + private string errorString; + + private string warningString; + + private Guid projectIdGuid; + + private bool isClosed; + + private EventTriggering eventTriggeringFlag = EventTriggering.TriggerAll; + + private bool invokeMSBuildWhenResumed; + + private uint suspendMSBuildCounter; + + private bool canFileNodesHaveChilds; + + private bool isProjectEventsListener = true; + + /// + /// The build dependency list passed to IVsDependencyProvider::EnumDependencies + /// + private List buildDependencyList = new List(); + + /// + /// Defines if Project System supports Project Designer + /// + private bool supportsProjectDesigner; + + private bool showProjectInSolutionPage = true; + + private bool buildInProcess; + + /// + /// Field for determining whether sourcecontrol should be disabled. + /// + private bool disableScc; + + private string sccProjectName; + + private string sccLocalPath; + + private string sccAuxPath; + + private string sccProvider; + + /// + /// Flag for controling how many times we register with the Scc manager. + /// + private bool isRegisteredWithScc; + + /// + /// Flag for controling query edit should communicate with the scc manager. + /// + private bool disableQueryEdit; + + /// + /// Control if command with potential destructive behavior such as delete should + /// be enabled for nodes of this project. + /// + private bool canProjectDeleteItems; + + /// + /// Token processor used by the project sample. + /// + private TokenProcessor tokenProcessor; + + /// + /// Member to store output base relative path. Used by OutputBaseRelativePath property + /// + private string outputBaseRelativePath = "bin"; + + private IProjectEvents projectEventsProvider; + + /// + /// Used for flavoring to hold the XML fragments + /// + private XmlDocument xmlFragments; + + /// + /// Used to map types to CATID. This provide a generic way for us to do this + /// and make it simpler for a project to provide it's CATIDs for the different type of objects + /// for which it wants to support extensibility. This also enables us to have multiple + /// type mapping to the same CATID if we choose to. + /// + private Dictionary catidMapping = new Dictionary(); + + /// + /// The internal package implementation. + /// + private ProjectPackage package; + + // Has the object been disposed. + private bool isDisposed; + #endregion + + #region abstract properties + /// + /// This Guid must match the Guid you registered under + /// HKLM\Software\Microsoft\VisualStudio\%version%\Projects. + /// Among other things, the Project framework uses this + /// guid to find your project and item templates. + /// + public abstract Guid ProjectGuid + { + get; + } + + /// + /// Returns a caption for VSHPROPID_TypeName. + /// + /// + public abstract string ProjectType + { + get; + } + #endregion + + #region virtual properties + /// + /// This is the project instance guid that is peristed in the project file + /// + [System.ComponentModel.BrowsableAttribute(false)] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ID")] + public virtual Guid ProjectIDGuid + { + get + { + return this.projectIdGuid; + } + set + { + if (this.projectIdGuid != value) + { + this.projectIdGuid = value; + if (this.buildProject != null) + { + this.SetProjectProperty("ProjectGuid", this.projectIdGuid.ToString("B")); + } + } + } + } + #endregion + + #region properties + + + #region overridden properties + public override int MenuCommandId + { + get + { + return VsMenus.IDM_VS_CTXT_PROJNODE; + } + } + + public override string Url + { + get + { + return this.GetMkDocument(); + } + } + + public override string Caption + { + get + { + // Default to file name + string caption = this.buildProject.FullPath; + if (String.IsNullOrEmpty(caption)) + { + if (this.buildProject.GetProperty(ProjectFileConstants.Name) != null) + { + caption = this.buildProject.GetProperty(ProjectFileConstants.Name).EvaluatedValue; + if (caption == null || caption.Length == 0) + { + caption = this.ItemNode.GetMetadata(ProjectFileConstants.Include); + } + } + } + else + { + caption = Path.GetFileNameWithoutExtension(caption); + } + + return caption; + } + } + + public override Guid ItemTypeGuid + { + get + { + return this.ProjectGuid; + } + } + + public override int ImageIndex + { + get + { + return (int)ProjectNode.ImageName.Application; + } + } + + + #endregion + + #region virtual properties + + public virtual string ErrorString + { + get + { + if (this.errorString == null) + { + this.errorString = SR.GetString(SR.Error, CultureInfo.CurrentUICulture); + } + + return this.errorString; + } + } + + public virtual string WarningString + { + get + { + if (this.warningString == null) + { + this.warningString = SR.GetString(SR.Warning, CultureInfo.CurrentUICulture); + } + + return this.warningString; + } + } + + /// + /// The target name that will be used for evaluating the project file (i.e., pseudo-builds). + /// This target is used to trigger a build with when the project system changes. + /// Example: The language projrcts are triggering a build with the Compile target whenever + /// the project system changes. + /// + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ReEvaluate")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Re")] + protected internal virtual string ReEvaluateProjectFileTargetName + { + get + { + return null; + } + } + + /// + /// This is the object that will be returned by EnvDTE.Project.Object for this project + /// + protected internal virtual object ProjectObject + { + get + { + return null; + } + } + + /// + /// Override this property to specify when the project file is dirty. + /// + protected virtual bool IsProjectFileDirty + { + get + { + string document = this.GetMkDocument(); + + if (String.IsNullOrEmpty(document)) + { + return this.isDirty; + } + + return (this.isDirty || !File.Exists(document)); + } + } + + /// + /// True if the project uses the Project Designer Editor instead of the property page frame to edit project properties. + /// + protected virtual bool SupportsProjectDesigner + { + get + { + return this.supportsProjectDesigner; + } + set + { + this.supportsProjectDesigner = value; + } + + } + + protected virtual Guid ProjectDesignerEditor + { + get + { + return VSConstants.GUID_ProjectDesignerEditor; + } + } + + /// + /// Defines the flag that supports the VSHPROPID.ShowProjInSolutionPage + /// + protected virtual bool ShowProjectInSolutionPage + { + get + { + return this.showProjectInSolutionPage; + } + set + { + this.showProjectInSolutionPage = value; + } + } + + #endregion + + /// + /// Gets or sets the ability of a project filenode to have child nodes (sub items). + /// Example would be C#/VB forms having resx and designer files. + /// + protected internal bool CanFileNodesHaveChilds + { + get + { + return canFileNodesHaveChilds; + } + set + { + canFileNodesHaveChilds = value; + } + } + + /// + /// Get and set the Token processor. + /// + public TokenProcessor FileTemplateProcessor + { + get + { + if (tokenProcessor == null) + tokenProcessor = new TokenProcessor(); + return tokenProcessor; + } + set + { + tokenProcessor = value; + } + } + + /// + /// Gets a service provider object provided by the IDE hosting the project + /// + [SuppressMessage("Microsoft.Naming", "CA1721:PropertyNamesShouldNotMatchGetMethods")] + public IServiceProvider Site + { + get + { + return this.site; + } + } + + /// + /// Gets an ImageHandler for the project node. + /// + public ImageHandler ImageHandler + { + get + { + if (null == imageHandler) + { + imageHandler = new ImageHandler(typeof(ProjectNode).Assembly.GetManifestResourceStream("Microsoft.VisualStudio.Project.Resources.imagelis.bmp")); + } + return imageHandler; + } + } + + /// + /// This property returns the time of the last change made to this project. + /// It is not the time of the last change on the project file, but actually of + /// the in memory project settings. In other words, it is the last time that + /// SetProjectDirty was called. + /// + public DateTime LastModifiedTime + { + get + { + return this.lastModifiedTime; + } + } + + /// + /// Determines whether this project is a new project. + /// + public bool IsNewProject + { + get + { + return this.isNewProject; + } + } + + /// + /// Gets the path to the folder containing the project. + /// + public string ProjectFolder + { + get + { + return Path.GetDirectoryName(this.filename); + } + } + + /// + /// Gets or sets the project filename. + /// + public string ProjectFile + { + get + { + return Path.GetFileName(this.filename); + } + set + { + this.SetEditLabel(value); + } + } + + /// + /// Gets the Base Uniform Resource Identifier (URI). + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "URI")] + public Microsoft.VisualStudio.Shell.Url BaseURI + { + get + { + if (baseUri == null && this.buildProject != null) + { + string path = System.IO.Path.GetDirectoryName(this.buildProject.FullPath); + // Uri/Url behave differently when you have trailing slash and when you dont + if (!path.EndsWith("\\", StringComparison.Ordinal) && !path.EndsWith("/", StringComparison.Ordinal)) + path += "\\"; + baseUri = new Url(path); + } + + Debug.Assert(baseUri != null, "Base URL should not be null. Did you call BaseURI before loading the project?"); + return baseUri; + } + } + + /// + /// Gets whether or not the project is closed. + /// + public bool IsClosed + { + get + { + return this.isClosed; + } + } + + /// + /// Gets whether or not the project is being built. + /// + public bool BuildInProgress + { + get + { + return buildInProcess; + } + } + + /// + /// Gets or set the relative path to the folder containing the project ouput. + /// + public virtual string OutputBaseRelativePath + { + get + { + return this.outputBaseRelativePath; + } + set + { + if (Path.IsPathRooted(value)) + { + throw new ArgumentException("Path must not be rooted."); + } + + this.outputBaseRelativePath = value; + } + } + + /// + /// Gets or sets the flag whether query edit should communicate with the scc manager. + /// + protected bool DisableQueryEdit + { + get + { + return this.disableQueryEdit; + } + set + { + this.disableQueryEdit = value; + } + } + + /// + /// Gets a collection of integer ids that maps to project item instances + /// + internal EventSinkCollection ItemIdMap + { + get + { + return this.itemIdMap; + } + } + + /// + /// Get the helper object that track document changes. + /// + internal TrackDocumentsHelper Tracker + { + get + { + return this.tracker; + } + } + + /// + /// Gets or sets the build logger. + /// + protected Microsoft.Build.Utilities.Logger BuildLogger + { + get + { + return this.buildLogger; + } + set + { + this.buildLogger = value; + this.useProvidedLogger = true; + } + } + + /// + /// Gets the taskprovider. + /// + protected TaskProvider TaskProvider + { + get + { + return this.taskProvider; + } + } + + /// + /// Gets the project file name. + /// + protected string FileName + { + get + { + return this.filename; + } + } + + + /// + /// Gets the configuration provider. + /// + protected ConfigProvider ConfigProvider + { + get + { + if (this.configProvider == null) + { + this.configProvider = CreateConfigProvider(); + } + + return this.configProvider; + } + } + + /// + /// Gets or sets whether or not source code control is disabled for this project. + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")] + protected bool IsSccDisabled + { + get + { + return this.disableScc; + } + set + { + this.disableScc = value; + } + } + + /// + /// Gets or set whether items can be deleted for this project. + /// Enabling this feature can have the potential destructive behavior such as deleting files from disk. + /// + protected internal bool CanProjectDeleteItems + { + get + { + return canProjectDeleteItems; + } + set + { + canProjectDeleteItems = value; + } + } + + /// + /// Determines whether the project was fully opened. This is set when the OnAfterOpenProject has triggered. + /// + protected internal bool HasProjectOpened + { + get + { + return this.projectOpened; + } + } + + /// + /// Gets or sets event triggering flags. + /// + internal EventTriggering EventTriggeringFlag + { + get + { + return this.eventTriggeringFlag; + } + set + { + this.eventTriggeringFlag = value; + } + } + + /// + /// Defines the build project that has loaded the project file. + /// + protected internal MSBuild.Project BuildProject + { + get + { + return this.buildProject; + } + set + { + SetBuildProject(value); + } + } + + /// + /// Gets the current config. + /// + /// The current config. + protected internal Microsoft.Build.Execution.ProjectInstance CurrentConfig + { + get { return this.currentConfig; } + } + + /// + /// Defines the build engine that is used to build the project file. + /// + internal MSBuild.ProjectCollection BuildEngine + { + get + { + return this.buildEngine; + } + set + { + this.buildEngine = value; + } + } + + /// + /// The internal package implementation. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal ProjectPackage Package + { + get + { + return this.package; + } + set + { + this.package = value; + } + } + #endregion + + #region ctor + + protected ProjectNode() + { + this.Initialize(); + } + #endregion + + #region overridden methods + protected override NodeProperties CreatePropertiesObject() + { + return new ProjectNodeProperties(this); + } + + /// + /// Sets the properties for the project node. + /// + /// Identifier of the hierarchy property. For a list of propid values, + /// The value to set. + /// A success or failure value. + public override int SetProperty(int propid, object value) + { + __VSHPROPID id = (__VSHPROPID)propid; + + switch (id) + { + case __VSHPROPID.VSHPROPID_ShowProjInSolutionPage: + this.ShowProjectInSolutionPage = (bool)value; + return VSConstants.S_OK; + } + + return base.SetProperty(propid, value); + } + + /// + /// Renames the project node. + /// + /// The new name + /// A success or failure value. + public override int SetEditLabel(string label) + { + // Validate the filename. + if (String.IsNullOrEmpty(label)) + { + throw new InvalidOperationException(SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture)); + } + else if (this.ProjectFolder.Length + label.Length + 1 > NativeMethods.MAX_PATH) + { + throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.PathTooLong, CultureInfo.CurrentUICulture), label)); + } + else if (Utilities.IsFileNameInvalid(label)) + { + throw new InvalidOperationException(SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture)); + } + + string fileName = Path.GetFileNameWithoutExtension(label); + + // if there is no filename or it starts with a leading dot issue an error message and quit. + if (String.IsNullOrEmpty(fileName) || fileName[0] == '.') + { + throw new InvalidOperationException(SR.GetString(SR.FileNameCannotContainALeadingPeriod, CultureInfo.CurrentUICulture)); + } + + // Nothing to do if the name is the same + string oldFileName = Path.GetFileNameWithoutExtension(this.Url); + if (String.Compare(oldFileName, label, StringComparison.Ordinal) == 0) + { + return VSConstants.S_FALSE; + } + + // Now check whether the original file is still there. It could have been renamed. + if (!File.Exists(this.Url)) + { + throw new InvalidOperationException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.FileOrFolderCannotBeFound, CultureInfo.CurrentUICulture), this.ProjectFile)); + } + + // Get the full file name and then rename the project file. + string newFile = Path.Combine(this.ProjectFolder, label); + string extension = Path.GetExtension(this.Url); + + // Make sure it has the correct extension + if (String.Compare(Path.GetExtension(newFile), extension, StringComparison.OrdinalIgnoreCase) != 0) + { + newFile += extension; + } + + this.RenameProjectFile(newFile); + return VSConstants.S_OK; + } + + /// + /// Gets the automation object for the project node. + /// + /// An instance of an EnvDTE.Project implementation object representing the automation object for the project. + public override object GetAutomationObject() + { + return new Automation.OAProject(this); + } + + /// + /// Closes the project node. + /// + /// A success or failure value. + public override int Close() + { + int hr = VSConstants.S_OK; + try + { + // Walk the tree and close all nodes. + // This has to be done before the project closes, since we want still state available for the ProjectMgr on the nodes + // when nodes are closing. + try + { + CloseAllNodes(this); + } + finally + { + this.Dispose(true); + } + } + catch (COMException e) + { + hr = e.ErrorCode; + } + finally + { + ErrorHandler.ThrowOnFailure(base.Close()); + } + + this.isClosed = true; + + return hr; + } + + /// + /// Sets the service provider from which to access the services. + /// + /// An instance to an Microsoft.VisualStudio.OLE.Interop object + /// A success or failure value. + public override int SetSite(Microsoft.VisualStudio.OLE.Interop.IServiceProvider site) + { + CCITracing.TraceCall(); + this.site = new ServiceProvider(site); + + if (taskProvider != null) + { + taskProvider.Dispose(); + } + taskProvider = new TaskProvider(this.site); + + return VSConstants.S_OK; + } + + /// + /// Gets the properties of the project node. + /// + /// The __VSHPROPID of the property. + /// A property dependent value. See: for details. + public override object GetProperty(int propId) + { + switch ((__VSHPROPID)propId) + { + case __VSHPROPID.VSHPROPID_ConfigurationProvider: + return this.ConfigProvider; + + case __VSHPROPID.VSHPROPID_ProjectName: + return this.Caption; + + case __VSHPROPID.VSHPROPID_ProjectDir: + return this.ProjectFolder; + + case __VSHPROPID.VSHPROPID_TypeName: + return this.ProjectType; + + case __VSHPROPID.VSHPROPID_ShowProjInSolutionPage: + return this.ShowProjectInSolutionPage; + + case __VSHPROPID.VSHPROPID_ExpandByDefault: + return true; + + // Use the same icon as if the folder was closed + case __VSHPROPID.VSHPROPID_OpenFolderIconIndex: + return GetProperty((int)__VSHPROPID.VSHPROPID_IconIndex); + } + + switch ((__VSHPROPID2)propId) + { + case __VSHPROPID2.VSHPROPID_SupportsProjectDesigner: + return this.SupportsProjectDesigner; + + case __VSHPROPID2.VSHPROPID_PropertyPagesCLSIDList: + return Utilities.CreateSemicolonDelimitedListOfStringFromGuids(this.GetConfigurationIndependentPropertyPages()); + + case __VSHPROPID2.VSHPROPID_CfgPropertyPagesCLSIDList: + return Utilities.CreateSemicolonDelimitedListOfStringFromGuids(this.GetConfigurationDependentPropertyPages()); + + case __VSHPROPID2.VSHPROPID_PriorityPropertyPagesCLSIDList: + return Utilities.CreateSemicolonDelimitedListOfStringFromGuids(this.GetPriorityProjectDesignerPages()); + + case __VSHPROPID2.VSHPROPID_Container: + return true; + default: + break; + } + + return base.GetProperty(propId); + } + + /// + /// Gets the GUID value of the node. + /// + /// A __VSHPROPID or __VSHPROPID2 value of the guid property + /// The guid to return for the property. + /// A success or failure value. + public override int GetGuidProperty(int propid, out Guid guid) + { + guid = Guid.Empty; + if ((__VSHPROPID)propid == __VSHPROPID.VSHPROPID_ProjectIDGuid) + { + guid = this.ProjectIDGuid; + } + else if (propid == (int)__VSHPROPID.VSHPROPID_CmdUIGuid) + { + guid = this.ProjectGuid; + } + else if ((__VSHPROPID2)propid == __VSHPROPID2.VSHPROPID_ProjectDesignerEditor && this.SupportsProjectDesigner) + { + guid = this.ProjectDesignerEditor; + } + else + { + base.GetGuidProperty(propid, out guid); + } + + if (guid.CompareTo(Guid.Empty) == 0) + { + return VSConstants.DISP_E_MEMBERNOTFOUND; + } + + return VSConstants.S_OK; + } + + /// + /// Sets Guid properties for the project node. + /// + /// A __VSHPROPID or __VSHPROPID2 value of the guid property + /// The guid value to set. + /// A success or failure value. + public override int SetGuidProperty(int propid, ref Guid guid) + { + switch ((__VSHPROPID)propid) + { + case __VSHPROPID.VSHPROPID_ProjectIDGuid: + this.ProjectIDGuid = guid; + return VSConstants.S_OK; + } + CCITracing.TraceCall(String.Format(CultureInfo.CurrentCulture, "Property {0} not found", propid)); + return VSConstants.DISP_E_MEMBERNOTFOUND; + } + + /// + /// Removes items from the hierarchy. + /// + /// Project overwrites this. + public override void Remove(bool removeFromStorage) + { + // the project will not be deleted from disk, just removed + if (removeFromStorage) + { + return; + } + + // Remove the entire project from the solution + IVsSolution solution = this.Site.GetService(typeof(SVsSolution)) as IVsSolution; + uint iOption = 1; // SLNSAVEOPT_PromptSave + ErrorHandler.ThrowOnFailure(solution.CloseSolutionElement(iOption, this, 0)); + } + + /// + /// Gets the moniker for the project node. That is the full path of the project file. + /// + /// The moniker for the project file. + public override string GetMkDocument() + { + Debug.Assert(!String.IsNullOrEmpty(this.filename)); + Debug.Assert(this.BaseURI != null && !String.IsNullOrEmpty(this.BaseURI.AbsoluteUrl)); + return Path.Combine(this.BaseURI.AbsoluteUrl, this.filename); + } + + /// + /// Disposes the project node object. + /// + /// Flag determining ehether it was deterministic or non deterministic clean up. + protected override void Dispose(bool disposing) + { + if (this.isDisposed) + { + return; + } + + try + { + try + { + this.UnRegisterProject(); + } + finally + { + try + { + this.RegisterClipboardNotifications(false); + } + finally + { + try + { + if (this.projectEventsProvider != null) + { + this.projectEventsProvider.AfterProjectFileOpened -= this.OnAfterProjectOpen; + } + if (this.taskProvider != null) + { + taskProvider.Tasks.Clear(); + this.taskProvider.Dispose(); + this.taskProvider = null; + } + + if (this.buildLogger != null) + { + this.buildLogger.Shutdown(); + buildLogger = null; + } + + if (this.site != null) + { + this.site.Dispose(); + } + } + finally + { + this.buildEngine = null; + } + } + } + + if (this.buildProject != null) + { + this.buildProject.ProjectCollection.UnloadProject(this.buildProject); + this.buildProject.ProjectCollection.UnloadProject(this.buildProject.Xml); + this.buildProject = null; + } + + if (null != imageHandler) + { + imageHandler.Close(); + imageHandler = null; + } + } + finally + { + base.Dispose(disposing); + this.isDisposed = true; + } + } + + /// + /// Handles command status on the project node. If a command cannot be handled then the base should be called. + /// + /// A unique identifier of the command group. The pguidCmdGroup parameter can be NULL to specify the standard group. + /// The command to query status for. + /// Pointer to an OLECMDTEXT structure in which to return the name and/or status information of a single command. Can be NULL to indicate that the caller does not require this information. + /// An out parameter specifying the QueryStatusResult of the command. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + protected override int QueryStatusOnNode(Guid cmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result) + { + if (cmdGroup == VsMenus.guidStandardCommandSet97) + { + switch ((VsCommands)cmd) + { + case VsCommands.Copy: + case VsCommands.Paste: + case VsCommands.Cut: + case VsCommands.Rename: + case VsCommands.Exit: + case VsCommands.ProjectSettings: + case VsCommands.BuildSln: + case VsCommands.UnloadProject: + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + + case VsCommands.ViewForm: + if (this.HasDesigner) + { + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + } + break; + + case VsCommands.CancelBuild: + result |= QueryStatusResult.SUPPORTED; + if (this.buildInProcess) + result |= QueryStatusResult.ENABLED; + else + result |= QueryStatusResult.INVISIBLE; + return VSConstants.S_OK; + + case VsCommands.NewFolder: + case VsCommands.AddNewItem: + case VsCommands.AddExistingItem: + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + + case VsCommands.SetStartupProject: + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + } + } + else if (cmdGroup == VsMenus.guidStandardCommandSet2K) + { + + switch ((VsCommands2K)cmd) + { + case VsCommands2K.ADDREFERENCE: + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + + case VsCommands2K.EXCLUDEFROMPROJECT: + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.INVISIBLE; + return VSConstants.S_OK; + } + } + else + { + return (int)OleConstants.OLECMDERR_E_UNKNOWNGROUP; + } + + return base.QueryStatusOnNode(cmdGroup, cmd, pCmdText, ref result); + } + + /// + /// Handles command execution. + /// + /// Unique identifier of the command group + /// The command to be executed. + /// Values describe how the object should execute the command. + /// Pointer to a VARIANTARG structure containing input arguments. Can be NULL + /// VARIANTARG structure to receive command output. Can be NULL. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + protected override int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) + { + if (cmdGroup == VsMenus.guidStandardCommandSet97) + { + switch ((VsCommands)cmd) + { + + case VsCommands.UnloadProject: + return this.UnloadProject(); + case VsCommands.CleanSel: + case VsCommands.CleanCtx: + return this.CleanProject(); + } + } + else if (cmdGroup == VsMenus.guidStandardCommandSet2K) + { + switch ((VsCommands2K)cmd) + { + case VsCommands2K.ADDREFERENCE: + return this.AddProjectReference(); + + case VsCommands2K.ADDWEBREFERENCE: + case VsCommands2K.ADDWEBREFERENCECTX: + return this.AddWebReference(); + } + } + + return base.ExecCommandOnNode(cmdGroup, cmd, nCmdexecopt, pvaIn, pvaOut); + } + + /// + /// Get the boolean value for the deletion of a project item + /// + /// A flag that specifies the type of delete operation (delete from storage or remove from project) + /// true if item can be deleted from project + protected override bool CanDeleteItem(__VSDELETEITEMOPERATION deleteOperation) + { + if (deleteOperation == __VSDELETEITEMOPERATION.DELITEMOP_RemoveFromProject) + { + return true; + } + return false; + } + + /// + /// Returns a specific Document manager to handle opening and closing of the Project(Application) Designer if projectdesigner is supported. + /// + /// Document manager object + protected internal override DocumentManager GetDocumentManager() + { + if (this.SupportsProjectDesigner) + { + return new ProjectDesignerDocumentManager(this); + } + return null; + } + + #endregion + + #region virtual methods + + /// + /// Executes a wizard. + /// + /// The node to which the wizard should add item(s). + /// The name of the file that the user typed in. + /// The name of the wizard to run. + /// The owner of the dialog box. + /// A VSADDRESULT enum value describing success or failure. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily"), SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "dlg")] + public virtual VSADDRESULT RunWizard(HierarchyNode parentNode, string itemName, string wizardToRun, IntPtr dlgOwner) + { + Debug.Assert(!String.IsNullOrEmpty(itemName), "The Add item dialog was passing in a null or empty item to be added to the hierrachy."); + Debug.Assert(!String.IsNullOrEmpty(this.ProjectFolder), "The Project Folder is not specified for this project."); + + if (parentNode == null) + { + throw new ArgumentNullException("parentNode"); + } + + if (String.IsNullOrEmpty(itemName)) + { + throw new ArgumentNullException("itemName"); + } + + // We just validate for length, since we assume other validation has been performed by the dlgOwner. + if (this.ProjectFolder.Length + itemName.Length + 1 > NativeMethods.MAX_PATH) + { + string errorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.PathTooLong, CultureInfo.CurrentUICulture), itemName); + if (!Utilities.IsInAutomationFunction(this.Site)) + { + string title = null; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + VsShellUtilities.ShowMessageBox(this.Site, title, errorMessage, icon, buttons, defaultButton); + return VSADDRESULT.ADDRESULT_Failure; + } + else + { + throw new InvalidOperationException(errorMessage); + } + } + + + // Build up the ContextParams safearray + // [0] = Wizard type guid (bstr) + // [1] = Project name (bstr) + // [2] = ProjectItems collection (bstr) + // [3] = Local Directory (bstr) + // [4] = Filename the user typed (bstr) + // [5] = Product install Directory (bstr) + // [6] = Run silent (bool) + + object[] contextParams = new object[7]; + contextParams[0] = EnvDTE.Constants.vsWizardAddItem; + contextParams[1] = this.Caption; + object automationObject = parentNode.GetAutomationObject(); + if (automationObject is EnvDTE.Project) + { + EnvDTE.Project project = (EnvDTE.Project)automationObject; + contextParams[2] = project.ProjectItems; + } + else + { + // This would normally be a folder unless it is an item with subitems + EnvDTE.ProjectItem item = (EnvDTE.ProjectItem)automationObject; + contextParams[2] = item.ProjectItems; + } + + contextParams[3] = this.ProjectFolder; + + contextParams[4] = itemName; + + object objInstallationDir = null; + IVsShell shell = (IVsShell)this.GetService(typeof(IVsShell)); + ErrorHandler.ThrowOnFailure(shell.GetProperty((int)__VSSPROPID.VSSPROPID_InstallDirectory, out objInstallationDir)); + string installDir = (string)objInstallationDir; + + // append a '\' to the install dir to mimic what the shell does (though it doesn't add one to destination dir) + if (!installDir.EndsWith("\\", StringComparison.Ordinal)) + { + installDir += "\\"; + } + + contextParams[5] = installDir; + + contextParams[6] = true; + + IVsExtensibility3 ivsExtensibility = this.GetService(typeof(IVsExtensibility)) as IVsExtensibility3; + Debug.Assert(ivsExtensibility != null, "Failed to get IVsExtensibility3 service"); + if (ivsExtensibility == null) + { + return VSADDRESULT.ADDRESULT_Failure; + } + + // Determine if we have the trust to run this wizard. + IVsDetermineWizardTrust wizardTrust = this.GetService(typeof(SVsDetermineWizardTrust)) as IVsDetermineWizardTrust; + if (wizardTrust != null) + { + Guid guidProjectAdding = Guid.Empty; + ErrorHandler.ThrowOnFailure(wizardTrust.OnWizardInitiated(wizardToRun, ref guidProjectAdding)); + } + + int wizResultAsInt; + try + { + Array contextParamsAsArray = contextParams; + + int result = ivsExtensibility.RunWizardFile(wizardToRun, (int)dlgOwner, ref contextParamsAsArray, out wizResultAsInt); + + if (!ErrorHandler.Succeeded(result) && result != VSConstants.OLE_E_PROMPTSAVECANCELLED) + { + ErrorHandler.ThrowOnFailure(result); + } + } + finally + { + if (wizardTrust != null) + { + ErrorHandler.ThrowOnFailure(wizardTrust.OnWizardCompleted()); + } + } + + EnvDTE.wizardResult wizardResult = (EnvDTE.wizardResult)wizResultAsInt; + + switch (wizardResult) + { + default: + return VSADDRESULT.ADDRESULT_Cancel; + case wizardResult.wizardResultSuccess: + return VSADDRESULT.ADDRESULT_Success; + case wizardResult.wizardResultFailure: + return VSADDRESULT.ADDRESULT_Failure; + } + } + + /// + /// Override this method if you want to modify the behavior of the Add Reference dialog + /// By example you could change which pages are visible and which is visible by default. + /// + /// + public virtual int AddProjectReference() + { + CCITracing.TraceCall(); + + IVsComponentSelectorDlg componentDialog; + Guid guidEmpty = Guid.Empty; + VSCOMPONENTSELECTORTABINIT[] tabInit = new VSCOMPONENTSELECTORTABINIT[2]; + string strBrowseLocations = Path.GetDirectoryName(this.BaseURI.Uri.LocalPath); + + tabInit[0].dwSize = (uint)Marshal.SizeOf(typeof(VSCOMPONENTSELECTORTABINIT)); + // Tell the Add Reference dialog to call hierarchies GetProperty with the following + // propID to enablefiltering out ourself from the Project to Project reference + tabInit[0].varTabInitInfo = (int)__VSHPROPID.VSHPROPID_ShowProjInSolutionPage; + tabInit[0].guidTab = VSConstants.GUID_SolutionPage; + + // Add the Browse for file page + tabInit[1].dwSize = (uint)Marshal.SizeOf(typeof(VSCOMPONENTSELECTORTABINIT)); + tabInit[1].guidTab = VSConstants.GUID_BrowseFilePage; + tabInit[1].varTabInitInfo = 0; + + + componentDialog = this.GetService(typeof(IVsComponentSelectorDlg)) as IVsComponentSelectorDlg; + try + { + // call the container to open the add reference dialog. + if (componentDialog != null) + { + // Let the project know not to show itself in the Add Project Reference Dialog page + this.ShowProjectInSolutionPage = false; + + // call the container to open the add reference dialog. + string browseFilters = "Component files (*.dll)" + "\0*.dll\0"; + ErrorHandler.ThrowOnFailure(componentDialog.ComponentSelectorDlg( + (System.UInt32)(__VSCOMPSELFLAGS.VSCOMSEL_MultiSelectMode | __VSCOMPSELFLAGS.VSCOMSEL_IgnoreMachineName), + (IVsComponentUser)this, + SR.GetString(SR.AddReferenceDialogTitle, CultureInfo.CurrentUICulture), // Title + "VS.AddReference", // Help topic + ref guidEmpty, + ref guidEmpty, + String.Empty, // Machine Name + (uint)tabInit.Length, + tabInit, + browseFilters, + ref strBrowseLocations)); + } + } + catch (COMException e) + { + Trace.WriteLine("Exception : " + e.Message); + return e.ErrorCode; + } + finally + { + // Let the project know it can show itself in the Add Project Reference Dialog page + this.ShowProjectInSolutionPage = true; + } + return VSConstants.S_OK; + } + + /// + /// Returns the Compiler associated to the project + /// + /// Null + public virtual ICodeCompiler GetCompiler() + { + + return null; + } + + /// + /// Override this method if you have your own project specific + /// subclass of ProjectOptions + /// + /// This method returns a new instance of the ProjectOptions base class. + public virtual ProjectOptions CreateProjectOptions() + { + return new ProjectOptions(); + } + + /// + /// Loads a project file. Called from the factory CreateProject to load the project. + /// + /// File name of the project that will be created. + /// Location where the project will be created. + /// If applicable, the name of the template to use when cloning a new project. + /// Set of flag values taken from the VSCREATEPROJFLAGS enumeration. + /// Identifier of the interface that the caller wants returned. + /// An out parameter specifying if the project creation was canceled + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "iid")] + public virtual void Load(string fileName, string location, string name, uint flags, ref Guid iidProject, out int canceled) + { + try + { + this.disableQueryEdit = true; + + // set up internal members and icons + canceled = 0; + + this.ProjectMgr = this; + this.isNewProject = false; + + if ((flags & (uint)__VSCREATEPROJFLAGS.CPF_CLONEFILE) == (uint)__VSCREATEPROJFLAGS.CPF_CLONEFILE) + { + // we need to generate a new guid for the project + this.projectIdGuid = Guid.NewGuid(); + } + else + { + this.SetProjectGuidFromProjectFile(); + } + + // This is almost a No op if the engine has already been instantiated in the factory. + this.buildEngine = Utilities.InitializeMsBuildEngine(this.buildEngine, this.Site); + + // based on the passed in flags, this either reloads/loads a project, or tries to create a new one + // now we create a new project... we do that by loading the template and then saving under a new name + // we also need to copy all the associated files with it. + if ((flags & (uint)__VSCREATEPROJFLAGS.CPF_CLONEFILE) == (uint)__VSCREATEPROJFLAGS.CPF_CLONEFILE) + { + Debug.Assert(!String.IsNullOrEmpty(fileName) && File.Exists(fileName), "Invalid filename passed to load the project. A valid filename is expected"); + + this.isNewProject = true; + + // This should be a very fast operation if the build project is already initialized by the Factory. + SetBuildProject(Utilities.ReinitializeMsBuildProject(this.buildEngine, fileName, this.buildProject)); + + // Compute the file name + // We try to solve two problems here. When input comes from a wizzard in case of zipped based projects + // the parameters are different. + // In that case the filename has the new filename in a temporay path. + + // First get the extension from the template. + // Then get the filename from the name. + // Then create the new full path of the project. + string extension = Path.GetExtension(fileName); + + string tempName = String.Empty; + + // We have to be sure that we are not going to loose data here. If the project name is a.b.c then for a project that was based on a zipped template(the wizzard calls us) GetFileNameWithoutExtension will suppress "c". + // We are going to check if the parameter "name" is extension based and the extension is the same as the one from the "filename" parameter. + string tempExtension = Path.GetExtension(name); + if (!String.IsNullOrEmpty(tempExtension)) + { + bool isSameExtension = (String.Compare(tempExtension, extension, StringComparison.OrdinalIgnoreCase) == 0); + + if (isSameExtension) + { + tempName = Path.GetFileNameWithoutExtension(name); + } + // If the tempExtension is not the same as the extension that the project name comes from then assume that the project name is a dotted name. + else + { + tempName = Path.GetFileName(name); + } + } + else + { + tempName = Path.GetFileName(name); + } + + Debug.Assert(!String.IsNullOrEmpty(tempName), "Could not compute project name"); + string tempProjectFileName = tempName + extension; + this.filename = Path.Combine(location, tempProjectFileName); + + // Initialize the common project properties. + this.InitializeProjectProperties(); + + ErrorHandler.ThrowOnFailure(this.Save(this.filename, 1, 0)); + + // now we do have the project file saved. we need to create embedded files. + foreach (MSBuild.ProjectItem item in this.BuildProject.Items) + { + // Ignore the item if it is a reference or folder + if (this.FilterItemTypeToBeAddedToHierarchy(item.ItemType)) + { + continue; + } + + // MSBuilds tasks/targets can create items (such as object files), + // such items are not part of the project per say, and should not be displayed. + // so ignore those items. + if (!this.IsItemTypeFileType(item.ItemType)) + { + continue; + } + + string strRelFilePath = item.EvaluatedInclude; + string basePath = Path.GetDirectoryName(fileName); + string strPathToFile; + string newFileName; + // taking the base name from the project template + the relative pathname, + // and you get the filename + strPathToFile = Path.Combine(basePath, strRelFilePath); + // the new path should be the base dir of the new project (location) + the rel path of the file + newFileName = Path.Combine(location, strRelFilePath); + // now the copy file + AddFileFromTemplate(strPathToFile, newFileName); + } + } + else + { + this.filename = fileName; + } + + // now reload to fix up references + this.Reload(); + } + finally + { + this.disableQueryEdit = false; + } + } + + /// + /// Called to add a file to the project from a template. + /// Override to do it yourself if you want to customize the file + /// + /// Full path of template file + /// Full path of file once added to the project + public virtual void AddFileFromTemplate(string source, string target) + { + if (String.IsNullOrEmpty(source)) + { + throw new ArgumentNullException("source"); + } + if (String.IsNullOrEmpty(target)) + { + throw new ArgumentNullException("target"); + } + + try + { + string directory = Path.GetDirectoryName(target); + if (!String.IsNullOrEmpty(directory) && !Directory.Exists(directory)) + { + Directory.CreateDirectory(directory); + } + + FileInfo fiOrg = new FileInfo(source); + FileInfo fiNew = fiOrg.CopyTo(target, true); + + fiNew.Attributes = FileAttributes.Normal; // remove any read only attributes. + } + catch (IOException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch (UnauthorizedAccessException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch (ArgumentException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch (NotSupportedException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + } + + /// + /// Called when the project opens an editor window for the given file + /// + public virtual void OnOpenItem(string fullPathToSourceFile) + { + } + + /// + /// This add methos adds the "key" item to the hierarchy, potentially adding other subitems in the process + /// This method may recurse if the parent is an other subitem + /// + /// + /// List of subitems not yet added to the hierarchy + /// Key to retrieve the target item from the subitems list + /// Newly added node + /// If the parent node was found we add the dependent item to it otherwise we add the item ignoring the "DependentUpon" metatdata + protected virtual HierarchyNode AddDependentFileNode(IDictionary subitems, string key) + { + if (subitems == null) + { + throw new ArgumentNullException("subitems"); + } + + MSBuild.ProjectItem item = subitems[key]; + subitems.Remove(key); + + HierarchyNode newNode; + HierarchyNode parent = null; + + string dependentOf = item.GetMetadataValue(ProjectFileConstants.DependentUpon); + Debug.Assert(String.Compare(dependentOf, key, StringComparison.OrdinalIgnoreCase) != 0, "File dependent upon itself is not valid. Ignoring the DependentUpon metadata"); + if (subitems.ContainsKey(dependentOf)) + { + // The parent item is an other subitem, so recurse into this method to add the parent first + parent = AddDependentFileNode(subitems, dependentOf); + } + else + { + // See if the parent node already exist in the hierarchy + uint parentItemID; + string path = Path.Combine(this.ProjectFolder, dependentOf); + ErrorHandler.ThrowOnFailure(this.ParseCanonicalName(path, out parentItemID)); + if (parentItemID != 0) + parent = this.NodeFromItemId(parentItemID); + Debug.Assert(parent != null, "File dependent upon a non existing item or circular dependency. Ignoring the DependentUpon metadata"); + } + + // If the parent node was found we add the dependent item to it otherwise we add the item ignoring the "DependentUpon" metatdata + if (parent != null) + newNode = this.AddDependentFileNodeToNode(item, parent); + else + newNode = this.AddIndependentFileNode(item); + + return newNode; + } + + /// + /// This is called from the main thread before the background build starts. + /// cleanBuild is not part of the vsopts, but passed down as the callpath is differently + /// PrepareBuild mainly creates directories and cleans house if cleanBuild is true + /// + /// + /// + public virtual void PrepareBuild(string config, bool cleanBuild) + { + if (this.buildIsPrepared && !cleanBuild) return; + + ProjectOptions options = this.GetProjectOptions(config); + string outputPath = Path.GetDirectoryName(options.OutputAssembly); + + if (cleanBuild && this.currentConfig.Targets.ContainsKey(MsBuildTarget.Clean)) + { + this.InvokeMsBuild(MsBuildTarget.Clean); + } + + PackageUtilities.EnsureOutputPath(outputPath); + if (!String.IsNullOrEmpty(options.XmlDocFileName)) + { + PackageUtilities.EnsureOutputPath(Path.GetDirectoryName(options.XmlDocFileName)); + } + + this.buildIsPrepared = true; + } + + /// + /// Do the build by invoking msbuild + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "vsopts")] + public virtual MSBuildResult Build(uint vsopts, string config, IVsOutputWindowPane output, string target) + { + lock (ProjectNode.BuildLock) + { + bool engineLogOnlyCritical = this.BuildPrelude(output); + + MSBuildResult result = MSBuildResult.Failed; + + try + { + this.SetBuildConfigurationProperties(config); + + result = this.InvokeMsBuild(target); + } + finally + { + // Unless someone specifically request to use an output window pane, we should not output to it + if (null != output) + { + this.SetOutputLogger(null); + BuildEngine.OnlyLogCriticalEvents = engineLogOnlyCritical; + } + } + + return result; + } + } + + + /// + /// Do the build by invoking msbuild + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "vsopts")] + internal virtual void BuildAsync(uint vsopts, string config, IVsOutputWindowPane output, string target, Action uiThreadCallback) + { + this.BuildPrelude(output); + this.SetBuildConfigurationProperties(config); + this.DoMSBuildSubmission(BuildKind.Async, target, uiThreadCallback); + } + + /// + /// Return the value of a project property + /// + /// Name of the property to get + /// True to avoid using the cache + /// null if property does not exist, otherwise value of the property + public virtual string GetProjectProperty(string propertyName, bool resetCache) + { + MSBuildExecution.ProjectPropertyInstance property = GetMsBuildProperty(propertyName, resetCache); + if (property == null) + return null; + + return property.EvaluatedValue; + } + + /// + /// Set value of project property + /// + /// Name of property + /// Value of property + public virtual void SetProjectProperty(string propertyName, string propertyValue) + { + if (propertyName == null) + throw new ArgumentNullException("propertyName", "Cannot set a null project property"); + + string oldValue = null; + ProjectPropertyInstance oldProp = GetMsBuildProperty(propertyName, true); + if (oldProp != null) + oldValue = oldProp.EvaluatedValue; + if (propertyValue == null) + { + // if property already null, do nothing + if (oldValue == null) + return; + // otherwise, set it to empty + propertyValue = String.Empty; + } + + // Only do the work if this is different to what we had before + if (String.Compare(oldValue, propertyValue, StringComparison.Ordinal) != 0) + { + // Check out the project file. + if (!this.ProjectMgr.QueryEditProjectFile(false)) + { + throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + + this.buildProject.SetProperty(propertyName, propertyValue); + RaiseProjectPropertyChanged(propertyName, oldValue, propertyValue); + + // property cache will need to be updated + this.currentConfig = null; + this.SetProjectFileDirty(true); + } + return; + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")] + public virtual ProjectOptions GetProjectOptions(string config) + { + // This needs to be commented out because if you build for Debug the properties from the Debug + // config are cached. When you change configurations the old props are still cached, and + // building for release the properties from the Debug config are used. This may not be the best + // fix as every time you get properties the objects are reloaded, so for perf it is bad, but + // for making it work it is necessary (reload props when a config is changed?). + ////if(this.options != null) + //// return this.options; + + ProjectOptions options = CreateProjectOptions(); + + if (config == null) + return options; + + options.GenerateExecutable = true; + + this.SetConfiguration(config); + + string outputPath = this.GetOutputPath(this.currentConfig); + if (!String.IsNullOrEmpty(outputPath)) + { + // absolutize relative to project folder location + outputPath = Path.Combine(this.ProjectFolder, outputPath); + } + + // Set some default values + options.OutputAssembly = outputPath + this.Caption + ".exe"; + options.ModuleKind = ModuleKindFlags.ConsoleApplication; + + options.RootNamespace = GetProjectProperty(ProjectFileConstants.RootNamespace, false); + options.OutputAssembly = outputPath + this.GetAssemblyName(config); + + string outputtype = GetProjectProperty(ProjectFileConstants.OutputType, false); + if (!string.IsNullOrEmpty(outputtype)) + { + outputtype = outputtype.ToLower(CultureInfo.InvariantCulture); + } + + if (outputtype == "library") + { + options.ModuleKind = ModuleKindFlags.DynamicallyLinkedLibrary; + options.GenerateExecutable = false; // DLL's have no entry point. + } + else if (outputtype == "winexe") + options.ModuleKind = ModuleKindFlags.WindowsApplication; + else + options.ModuleKind = ModuleKindFlags.ConsoleApplication; + + options.Win32Icon = GetProjectProperty("ApplicationIcon", false); + options.MainClass = GetProjectProperty("StartupObject", false); + + string targetPlatform = GetProjectProperty("TargetPlatform", false); + + if (targetPlatform != null && targetPlatform.Length > 0) + { + try + { + options.TargetPlatform = (PlatformType)Enum.Parse(typeof(PlatformType), targetPlatform); + } + catch (ArgumentException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + options.TargetPlatformLocation = GetProjectProperty("TargetPlatformLocation", false); + this.SetTargetPlatform(options); + } + + // other settings from CSharp we may want to adopt at some point... + // AssemblyKeyContainerName = "" //This is the key file used to sign the interop assembly generated when importing a com object via add reference + // AssemblyOriginatorKeyFile = "" + // DelaySign = "false" + // DefaultClientScript = "JScript" + // DefaultHTMLPageLayout = "Grid" + // DefaultTargetSchema = "IE50" + // PreBuildEvent = "" + // PostBuildEvent = "" + // RunPostBuildEvent = "OnBuildSuccess" + + // transfer all config build options... + if (GetBoolAttr(this.currentConfig, "AllowUnsafeBlocks")) + { + options.AllowUnsafeCode = true; + } + + if (GetProjectProperty("BaseAddress", false) != null) + { + try + { + options.BaseAddress = Int64.Parse(GetProjectProperty("BaseAddress", false), CultureInfo.InvariantCulture); + } + catch (ArgumentNullException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch (ArgumentException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch (FormatException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch (OverflowException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + } + + if (GetBoolAttr(this.currentConfig, "CheckForOverflowUnderflow")) + { + options.CheckedArithmetic = true; + } + + if (GetProjectProperty("DefineConstants", false) != null) + { + options.DefinedPreprocessorSymbols = new StringCollection(); + foreach (string s in GetProjectProperty("DefineConstants", false).Replace(" \t\r\n", "").Split(';')) + { + options.DefinedPreprocessorSymbols.Add(s); + } + } + + string docFile = GetProjectProperty("DocumentationFile", false); + if (!String.IsNullOrEmpty(docFile)) + { + options.XmlDocFileName = Path.Combine(this.ProjectFolder, docFile); + } + + if (GetBoolAttr(this.currentConfig, "DebugSymbols")) + { + options.IncludeDebugInformation = true; + } + + if (GetProjectProperty("FileAlignment", false) != null) + { + try + { + options.FileAlignment = Int32.Parse(GetProjectProperty("FileAlignment", false), CultureInfo.InvariantCulture); + } + catch (ArgumentNullException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch (ArgumentException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch (FormatException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch (OverflowException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + } + + if (GetBoolAttr(this.currentConfig, "IncrementalBuild")) + { + options.IncrementalCompile = true; + } + + if (GetBoolAttr(this.currentConfig, "Optimize")) + { + options.Optimize = true; + } + + if (GetBoolAttr(this.currentConfig, "RegisterForComInterop")) + { + } + + if (GetBoolAttr(this.currentConfig, "RemoveIntegerChecks")) + { + } + + if (GetBoolAttr(this.currentConfig, "TreatWarningsAsErrors")) + { + options.TreatWarningsAsErrors = true; + } + + if (GetProjectProperty("WarningLevel", false) != null) + { + try + { + options.WarningLevel = Int32.Parse(GetProjectProperty("WarningLevel", false), CultureInfo.InvariantCulture); + } + catch (ArgumentNullException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch (ArgumentException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch (FormatException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch (OverflowException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + } + + return options; + } + + public virtual void SetTargetPlatform(ProjectOptions options) + { + } + + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Attr")] + [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "bool")] + public virtual bool GetBoolAttr(string config, string name) + { + this.SetConfiguration(config); + return this.GetBoolAttr(this.currentConfig, name); + } + + /// + /// Get the assembly name for a give configuration + /// + /// the matching configuration in the msbuild file + /// assembly name + public virtual string GetAssemblyName(string config) + { + this.SetConfiguration(config); + return GetAssemblyName(this.currentConfig); + } + + /// + /// Determines whether a file is a code file. + /// + /// Name of the file to be evaluated + /// false by default for any fileName + public virtual bool IsCodeFile(string fileName) + { + return false; + } + + /// + /// Determines whether the given file is a resource file (resx file). + /// + /// Name of the file to be evaluated. + /// true if the file is a resx file, otherwise false. + public virtual bool IsEmbeddedResource(string fileName) + { + if (String.Compare(Path.GetExtension(fileName), ".ResX", StringComparison.OrdinalIgnoreCase) == 0) + return true; + return false; + } + + /// + /// Create a file node based on an msbuild item. + /// + /// msbuild item + /// FileNode added + public virtual FileNode CreateFileNode(ProjectElement item) + { + return new FileNode(this, item); + } + + /// + /// Create a file node based on a string. + /// + /// filename of the new filenode + /// File node added + public virtual FileNode CreateFileNode(string file) + { + ProjectElement item = this.AddFileToMsBuild(file); + return this.CreateFileNode(item); + } + + /// + /// Create dependent file node based on an msbuild item + /// + /// msbuild item + /// dependent file node + public virtual DependentFileNode CreateDependentFileNode(ProjectElement item) + { + return new DependentFileNode(this, item); + } + + /// + /// Create a dependent file node based on a string. + /// + /// filename of the new dependent file node + /// Dependent node added + public virtual DependentFileNode CreateDependentFileNode(string file) + { + ProjectElement item = this.AddFileToMsBuild(file); + return this.CreateDependentFileNode(item); + } + + /// + /// Walks the subpaths of a project relative path and checks if the folder nodes hierarchy is already there, if not creates it. + /// + /// Path of the folder, can be relative to project or absolute + public virtual HierarchyNode CreateFolderNodes(string path) + { + if (String.IsNullOrEmpty(path)) + { + throw new ArgumentNullException("path"); + } + + if (Path.IsPathRooted(path)) + { + // Ensure we are using a relative path + if (String.Compare(this.ProjectFolder, 0, path, 0, this.ProjectFolder.Length, StringComparison.OrdinalIgnoreCase) != 0) + throw new ArgumentException("The path is not relative", "path"); + + path = path.Substring(this.ProjectFolder.Length); + } + + string[] parts; + HierarchyNode curParent; + + parts = path.Split(Path.DirectorySeparatorChar); + path = String.Empty; + curParent = this; + + // now we have an array of subparts.... + for (int i = 0; i < parts.Length; i++) + { + if (parts[i].Length > 0) + { + path += parts[i] + "\\"; + curParent = VerifySubFolderExists(path, curParent); + } + } + return curParent; + } + + /// + /// Defines if Node has Designer. By default we do not support designers for nodes + /// + /// Path to item to query for designer support + /// true if node has designer + public virtual bool NodeHasDesigner(string itemPath) + { + return false; + } + + /// + /// List of Guids of the config independent property pages. It is called by the GetProperty for VSHPROPID_PropertyPagesCLSIDList property. + /// + /// + protected virtual Guid[] GetConfigurationIndependentPropertyPages() + { + return new Guid[] { Guid.Empty }; + } + + /// + /// Returns a list of Guids of the configuration dependent property pages. It is called by the GetProperty for VSHPROPID_CfgPropertyPagesCLSIDList property. + /// + /// + protected virtual Guid[] GetConfigurationDependentPropertyPages() + { + return new Guid[0]; + } + + /// + /// An ordered list of guids of the prefered property pages. See + /// + /// An array of guids. + protected virtual Guid[] GetPriorityProjectDesignerPages() + { + return new Guid[] { Guid.Empty }; + } + + /// + /// Takes a path and verifies that we have a node with that name. + /// It is meant to be a helper method for CreateFolderNodes(). + /// For some scenario it may be useful to override. + /// + /// full path to the subfolder we want to verify. + /// the parent node where to add the subfolder if it does not exist. + /// the foldernode correcsponding to the path. + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "SubFolder")] + protected virtual FolderNode VerifySubFolderExists(string path, HierarchyNode parent) + { + FolderNode folderNode = null; + uint uiItemId; + Url url = new Url(this.BaseURI, path); + string strFullPath = url.AbsoluteUrl; + // Folders end in our storage with a backslash, so add one... + ErrorHandler.ThrowOnFailure(this.ParseCanonicalName(strFullPath, out uiItemId)); + if (uiItemId != 0) + { + Debug.Assert(this.NodeFromItemId(uiItemId) is FolderNode, "Not a FolderNode"); + folderNode = (FolderNode)this.NodeFromItemId(uiItemId); + } + + if (folderNode == null && path != null && parent != null) + { + // folder does not exist yet... + // We could be in the process of loading so see if msbuild knows about it + ProjectElement item = null; + foreach (MSBuild.ProjectItem folder in buildProject.GetItems(ProjectFileConstants.Folder)) + { + if (String.Compare(folder.EvaluatedInclude.TrimEnd('\\'), path.TrimEnd('\\'), StringComparison.OrdinalIgnoreCase) == 0) + { + item = new ProjectElement(this, folder, false); + break; + } + } + // If MSBuild did not know about it, create a new one + if (item == null) + item = this.AddFolderToMsBuild(path); + folderNode = this.CreateFolderNode(path, item); + parent.AddChild(folderNode); + } + + return folderNode; + } + + /// + /// To support virtual folders, override this method to return your own folder nodes + /// + /// Path to store for this folder + /// Element corresponding to the folder + /// A FolderNode that can then be added to the hierarchy + protected internal virtual FolderNode CreateFolderNode(string path, ProjectElement element) + { + FolderNode folderNode = new FolderNode(this, path, element); + return folderNode; + } + + /// + /// Gets the list of selected HierarchyNode objects + /// + /// A list of HierarchyNode objects + protected internal virtual IList GetSelectedNodes() + { + // Retrieve shell interface in order to get current selection + IVsMonitorSelection monitorSelection = this.GetService(typeof(IVsMonitorSelection)) as IVsMonitorSelection; + + if (monitorSelection == null) + { + throw new InvalidOperationException(); + } + + List selectedNodes = new List(); + IntPtr hierarchyPtr = IntPtr.Zero; + IntPtr selectionContainer = IntPtr.Zero; + try + { + // Get the current project hierarchy, project item, and selection container for the current selection + // If the selection spans multiple hierachies, hierarchyPtr is Zero + uint itemid; + IVsMultiItemSelect multiItemSelect = null; + ErrorHandler.ThrowOnFailure(monitorSelection.GetCurrentSelection(out hierarchyPtr, out itemid, out multiItemSelect, out selectionContainer)); + + // We only care if there are one ore more nodes selected in the tree + if (itemid != VSConstants.VSITEMID_NIL && hierarchyPtr != IntPtr.Zero) + { + IVsHierarchy hierarchy = Marshal.GetObjectForIUnknown(hierarchyPtr) as IVsHierarchy; + + if (itemid != VSConstants.VSITEMID_SELECTION) + { + // This is a single selection. Compare hirarchy with our hierarchy and get node from itemid + if (Utilities.IsSameComObject(this, hierarchy)) + { + HierarchyNode node = this.NodeFromItemId(itemid); + if (node != null) + { + selectedNodes.Add(node); + } + } + else + { + NestedProjectNode node = this.GetNestedProjectForHierarchy(hierarchy); + if (node != null) + { + selectedNodes.Add(node); + } + } + } + else if (multiItemSelect != null) + { + // This is a multiple item selection. + + //Get number of items selected and also determine if the items are located in more than one hierarchy + uint numberOfSelectedItems; + int isSingleHierarchyInt; + ErrorHandler.ThrowOnFailure(multiItemSelect.GetSelectionInfo(out numberOfSelectedItems, out isSingleHierarchyInt)); + bool isSingleHierarchy = (isSingleHierarchyInt != 0); + + // Now loop all selected items and add to the list only those that are selected within this hierarchy + if (!isSingleHierarchy || (isSingleHierarchy && Utilities.IsSameComObject(this, hierarchy))) + { + Debug.Assert(numberOfSelectedItems > 0, "Bad number of selected itemd"); + VSITEMSELECTION[] vsItemSelections = new VSITEMSELECTION[numberOfSelectedItems]; + uint flags = (isSingleHierarchy) ? (uint)__VSGSIFLAGS.GSI_fOmitHierPtrs : 0; + ErrorHandler.ThrowOnFailure(multiItemSelect.GetSelectedItems(flags, numberOfSelectedItems, vsItemSelections)); + foreach (VSITEMSELECTION vsItemSelection in vsItemSelections) + { + if (isSingleHierarchy || Utilities.IsSameComObject(this, vsItemSelection.pHier)) + { + HierarchyNode node = this.NodeFromItemId(vsItemSelection.itemid); + if (node != null) + { + selectedNodes.Add(node); + } + } + } + } + } + } + } + finally + { + if (hierarchyPtr != IntPtr.Zero) + { + Marshal.Release(hierarchyPtr); + } + if (selectionContainer != IntPtr.Zero) + { + Marshal.Release(selectionContainer); + } + } + + return selectedNodes; + } + + /// + /// Recursevily walks the hierarchy nodes and redraws the state icons + /// + protected internal override void UpdateSccStateIcons() + { + if (this.FirstChild == null) + { + return; + } + + for (HierarchyNode n = this.FirstChild; n != null; n = n.NextSibling) + { + n.UpdateSccStateIcons(); + } + } + + + /// + /// Handles the shows all objects command. + /// + /// + protected internal virtual int ShowAllFiles() + { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + /// + /// Handles the Add web reference command. + /// + /// + protected internal virtual int AddWebReference() + { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + /// + /// Unloads the project. + /// + /// + protected internal virtual int UnloadProject() + { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + /// + /// Handles the clean project command. + /// + /// + protected virtual int CleanProject() + { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + /// + /// Reload project from project file + /// + protected virtual void Reload() + { + Debug.Assert(this.buildEngine != null, "There is no build engine defined for this project"); + + try + { + this.disableQueryEdit = true; + + this.isClosed = false; + this.eventTriggeringFlag = ProjectNode.EventTriggering.DoNotTriggerHierarchyEvents | ProjectNode.EventTriggering.DoNotTriggerTrackerEvents; + + SetBuildProject(Utilities.ReinitializeMsBuildProject(this.buildEngine, this.filename, this.buildProject)); + + // Load the guid + this.SetProjectGuidFromProjectFile(); + + this.ProcessReferences(); + + this.ProcessFiles(); + + this.ProcessFolders(); + + this.LoadNonBuildInformation(); + + this.InitSccInfo(); + + this.RegisterSccProject(); + } + finally + { + this.SetProjectFileDirty(false); + this.eventTriggeringFlag = ProjectNode.EventTriggering.TriggerAll; + this.disableQueryEdit = false; + } + } + + /// + /// Renames the project file + /// + /// The full path of the new project file. + protected virtual void RenameProjectFile(string newFile) + { + IVsUIShell shell = this.Site.GetService(typeof(SVsUIShell)) as IVsUIShell; + Debug.Assert(shell != null, "Could not get the ui shell from the project"); + if (shell == null) + { + throw new InvalidOperationException(); + } + + // Do some name validation + if (Microsoft.VisualStudio.Project.Utilities.ContainsInvalidFileNameChars(newFile)) + { + throw new InvalidOperationException(SR.GetString(SR.ErrorInvalidProjectName, CultureInfo.CurrentUICulture)); + } + + // Figure out what the new full name is + string oldFile = this.Url; + + int canContinue = 0; + IVsSolution vsSolution = (IVsSolution)this.GetService(typeof(SVsSolution)); + if (ErrorHandler.Succeeded(vsSolution.QueryRenameProject(this, oldFile, newFile, 0, out canContinue)) + && canContinue != 0) + { + bool isFileSame = NativeMethods.IsSamePath(oldFile, newFile); + + // If file already exist and is not the same file with different casing + if (!isFileSame && File.Exists(newFile)) + { + // Prompt the user for replace + string message = SR.GetString(SR.FileAlreadyExists, newFile); + + if (!Utilities.IsInAutomationFunction(this.Site)) + { + if (!VsShellUtilities.PromptYesNo(message, null, OLEMSGICON.OLEMSGICON_WARNING, shell)) + { + throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + } + else + { + throw new InvalidOperationException(message); + } + + // Delete the destination file after making sure it is not read only + File.SetAttributes(newFile, FileAttributes.Normal); + File.Delete(newFile); + } + + SuspendFileChanges fileChanges = new SuspendFileChanges(this.Site, this.filename); + fileChanges.Suspend(); + try + { + // Actual file rename + this.SaveMSBuildProjectFileAs(newFile); + + this.SetProjectFileDirty(false); + + if (!isFileSame) + { + // Now that the new file name has been created delete the old one. + // TODO: Handle source control issues. + File.SetAttributes(oldFile, FileAttributes.Normal); + File.Delete(oldFile); + } + + this.OnPropertyChanged(this, (int)__VSHPROPID.VSHPROPID_Caption, 0); + + // Update solution + ErrorHandler.ThrowOnFailure(vsSolution.OnAfterRenameProject((IVsProject)this, oldFile, newFile, 0)); + + ErrorHandler.ThrowOnFailure(shell.RefreshPropertyBrowser(0)); + } + finally + { + fileChanges.Resume(); + } + } + else + { + throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + } + + /// + /// Called by the project to know if the item is a file (that is part of the project) + /// or an intermediate file used by the MSBuild tasks/targets + /// Override this method if your project has more types or different ones + /// + /// Type name + /// True = items of this type should be included in the project + protected virtual bool IsItemTypeFileType(string type) + { + if (String.Compare(type, BuildAction.Compile.ToString(), StringComparison.OrdinalIgnoreCase) == 0 + || String.Compare(type, BuildAction.Content.ToString(), StringComparison.OrdinalIgnoreCase) == 0 + || String.Compare(type, BuildAction.EmbeddedResource.ToString(), StringComparison.OrdinalIgnoreCase) == 0 + || String.Compare(type, BuildAction.None.ToString(), StringComparison.OrdinalIgnoreCase) == 0) + return true; + + // we don't know about this type, so ignore it. + return false; + } + + /// + /// Filter items that should not be processed as file items. Example: Folders and References. + /// + protected virtual bool FilterItemTypeToBeAddedToHierarchy(string itemType) + { + return (String.Compare(itemType, ProjectFileConstants.Reference, StringComparison.OrdinalIgnoreCase) == 0 + || String.Compare(itemType, ProjectFileConstants.ProjectReference, StringComparison.OrdinalIgnoreCase) == 0 + || String.Compare(itemType, ProjectFileConstants.COMReference, StringComparison.OrdinalIgnoreCase) == 0 + || String.Compare(itemType, ProjectFileConstants.Folder, StringComparison.OrdinalIgnoreCase) == 0 + || String.Compare(itemType, ProjectFileConstants.WebReference, StringComparison.OrdinalIgnoreCase) == 0 + || String.Compare(itemType, ProjectFileConstants.WebReferenceFolder, StringComparison.OrdinalIgnoreCase) == 0); + } + + /// + /// Associate window output pane to the build logger + /// + /// + protected virtual void SetOutputLogger(IVsOutputWindowPane output) + { + // Create our logger, if it was not specified + if (!this.useProvidedLogger || this.buildLogger == null) + { + // Because we may be aggregated, we need to make sure to get the outer IVsHierarchy + IntPtr unknown = IntPtr.Zero; + IVsHierarchy hierarchy = null; + try + { + unknown = Marshal.GetIUnknownForObject(this); + hierarchy = Marshal.GetTypedObjectForIUnknown(unknown, typeof(IVsHierarchy)) as IVsHierarchy; + } + finally + { + if (unknown != IntPtr.Zero) + Marshal.Release(unknown); + } + // Create the logger + this.BuildLogger = new IDEBuildLogger(output, this.TaskProvider, hierarchy); + + // To retrive the verbosity level, the build logger depends on the registry root + // (otherwise it will used an hardcoded default) + ILocalRegistry2 registry = this.GetService(typeof(SLocalRegistry)) as ILocalRegistry2; + if (null != registry) + { + string registryRoot; + ErrorHandler.ThrowOnFailure(registry.GetLocalRegistryRoot(out registryRoot)); + IDEBuildLogger logger = this.BuildLogger as IDEBuildLogger; + if (!String.IsNullOrEmpty(registryRoot) && (null != logger)) + { + logger.BuildVerbosityRegistryRoot = registryRoot; + logger.ErrorString = this.ErrorString; + logger.WarningString = this.WarningString; + } + } + } + else + { + ((IDEBuildLogger)this.BuildLogger).OutputWindowPane = output; + } + } + + /// + /// Set configuration properties for a specific configuration + /// + /// configuration name + protected virtual void SetBuildConfigurationProperties(string config) + { + ProjectOptions options = null; + + if (!String.IsNullOrEmpty(config)) + { + options = this.GetProjectOptions(config); + } + + if (options != null && this.buildProject != null) + { + // Make sure the project configuration is set properly + this.SetConfiguration(config); + } + } + + /// + /// This execute an MSBuild target for a design-time build. + /// + /// Name of the MSBuild target to execute + /// Result from executing the target (success/failure) + /// + /// If you depend on the items/properties generated by the target + /// you should be aware that any call to BuildTarget on any project + /// will reset the list of generated items/properties + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")] + protected virtual MSBuildResult InvokeMsBuild(string target) + { + MSBuildResult result = MSBuildResult.Failed; + const bool designTime = true; + bool requiresUIThread = UIThread.Instance.IsUIThread; // we don't run tasks that require calling the STA thread, so unless we're ON it, we don't need it. + + IVsBuildManagerAccessor accessor = this.Site.GetService(typeof(SVsBuildManagerAccessor)) as IVsBuildManagerAccessor; + BuildSubmission submission = null; + + try + { + // Do the actual Build + if (this.buildProject != null) + { + if (!TryBeginBuild(designTime, requiresUIThread)) + { + throw new InvalidOperationException("A build is already in progress."); + } + + string[] targetsToBuild = new string[target != null ? 1 : 0]; + if (target != null) + { + targetsToBuild[0] = target; + } + + currentConfig = BuildProject.CreateProjectInstance(); + + BuildRequestData requestData = new BuildRequestData(currentConfig, targetsToBuild, this.BuildProject.ProjectCollection.HostServices, BuildRequestDataFlags.ReplaceExistingProjectInstance); + submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData); + if (accessor != null) + { + ErrorHandler.ThrowOnFailure(accessor.RegisterLogger(submission.SubmissionId, this.buildLogger)); + } + + BuildResult buildResult = submission.Execute(); + + result = (buildResult.OverallResult == BuildResultCode.Success) ? MSBuildResult.Successful : MSBuildResult.Failed; + } + } + finally + { + EndBuild(submission, designTime, requiresUIThread); + } + + return result; + } + + /// + /// Start MSBuild build submission + /// + /// If buildKind is ASync, this method starts the submission and returns. uiThreadCallback will be called on UI thread once submissions completes. + /// if buildKind is Sync, this method executes the submission and runs uiThreadCallback + /// Is it a Sync or ASync build + /// target to build + /// project instance to build; if null, this.BuildProject.CreateProjectInstance() is used to populate + /// callback to be run UI thread + /// A Build submission instance. + protected virtual BuildSubmission DoMSBuildSubmission(BuildKind buildKind, string target, Action uiThreadCallback) + { + const bool designTime = false; + bool requiresUIThread = buildKind == BuildKind.Sync && UIThread.Instance.IsUIThread; // we don't run tasks that require calling the STA thread, so unless we're ON it, we don't need it. + + IVsBuildManagerAccessor accessor = (IVsBuildManagerAccessor)this.Site.GetService(typeof(SVsBuildManagerAccessor)); + if (accessor == null) + { + throw new InvalidOperationException(); + } + + if (!TryBeginBuild(designTime, requiresUIThread)) + { + if (uiThreadCallback != null) + { + uiThreadCallback(MSBuildResult.Failed, target); + } + + return null; + } + + string[] targetsToBuild = new string[target != null ? 1 : 0]; + if (target != null) + { + targetsToBuild[0] = target; + } + + MSBuildExecution.ProjectInstance projectInstance = BuildProject.CreateProjectInstance(); + + projectInstance.SetProperty(GlobalProperty.VisualStudioStyleErrors.ToString(), "true"); + projectInstance.SetProperty("UTFOutput", "true"); + projectInstance.SetProperty(GlobalProperty.BuildingInsideVisualStudio.ToString(), "true"); + + this.BuildProject.ProjectCollection.HostServices.SetNodeAffinity(projectInstance.FullPath, NodeAffinity.InProc); + BuildRequestData requestData = new BuildRequestData(projectInstance, targetsToBuild, this.BuildProject.ProjectCollection.HostServices, BuildRequestDataFlags.ReplaceExistingProjectInstance); + BuildSubmission submission = BuildManager.DefaultBuildManager.PendBuildRequest(requestData); + try + { + if (useProvidedLogger && buildLogger != null) + { + ErrorHandler.ThrowOnFailure(accessor.RegisterLogger(submission.SubmissionId, buildLogger)); + } + + if (buildKind == BuildKind.Async) + { + submission.ExecuteAsync(sub => + { + UIThread.Instance.Run(() => + { + this.FlushBuildLoggerContent(); + EndBuild(sub, designTime, requiresUIThread); + uiThreadCallback((sub.BuildResult.OverallResult == BuildResultCode.Success) ? MSBuildResult.Successful : MSBuildResult.Failed, target); + }); + }, null); + } + else + { + submission.Execute(); + EndBuild(submission, designTime, requiresUIThread); + MSBuildResult msbuildResult = (submission.BuildResult.OverallResult == BuildResultCode.Success) ? MSBuildResult.Successful : MSBuildResult.Failed; + if (uiThreadCallback != null) + { + uiThreadCallback(msbuildResult, target); + } + } + } + catch (Exception e) + { + Debug.Fail(e.ToString()); + EndBuild(submission, designTime, requiresUIThread); + if (uiThreadCallback != null) + { + uiThreadCallback(MSBuildResult.Failed, target); + } + + throw; + } + + return submission; + } + + /// + /// Initialize common project properties with default value if they are empty + /// + /// The following common project properties are defaulted to projectName (if empty): + /// AssemblyName, Name and RootNamespace. + /// If the project filename is not set then no properties are set + protected virtual void InitializeProjectProperties() + { + // Get projectName from project filename. Return if not set + string projectName = Path.GetFileNameWithoutExtension(this.filename); + if (String.IsNullOrEmpty(projectName)) + { + return; + } + + if (String.IsNullOrEmpty(GetProjectProperty(ProjectFileConstants.AssemblyName))) + { + SetProjectProperty(ProjectFileConstants.AssemblyName, projectName); + } + if (String.IsNullOrEmpty(GetProjectProperty(ProjectFileConstants.Name))) + { + SetProjectProperty(ProjectFileConstants.Name, projectName); + } + if (String.IsNullOrEmpty(GetProjectProperty(ProjectFileConstants.RootNamespace))) + { + SetProjectProperty(ProjectFileConstants.RootNamespace, projectName); + } + } + + /// + /// Factory method for configuration provider + /// + /// Configuration provider created + protected virtual ConfigProvider CreateConfigProvider() + { + return new ConfigProvider(this); + } + + /// + /// Factory method for reference container node + /// + /// ReferenceContainerNode created + protected virtual ReferenceContainerNode CreateReferenceContainerNode() + { + return new ReferenceContainerNode(this); + } + + /// + /// Saves the project file on a new name. + /// + /// The new name of the project file. + /// Success value or an error code. + protected virtual int SaveAs(string newFileName) + { + Debug.Assert(!String.IsNullOrEmpty(newFileName), "Cannot save project file for an empty or null file name"); + if (String.IsNullOrEmpty(newFileName)) + { + throw new ArgumentNullException("newFileName"); + } + + newFileName = newFileName.Trim(); + + string errorMessage = String.Empty; + + if (newFileName.Length > NativeMethods.MAX_PATH) + { + errorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.PathTooLong, CultureInfo.CurrentUICulture), newFileName); + } + else + { + string fileName = String.Empty; + + try + { + fileName = Path.GetFileNameWithoutExtension(newFileName); + } + // We want to be consistent in the error message and exception we throw. fileName could be for example #¤&%"¤&"% and that would trigger an ArgumentException on Path.IsRooted. + catch (ArgumentException) + { + errorMessage = SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture); + } + + if (errorMessage.Length == 0) + { + // If there is no filename or it starts with a leading dot issue an error message and quit. + // For some reason the save as dialog box allows to save files like "......ext" + if (String.IsNullOrEmpty(fileName) || fileName[0] == '.') + { + errorMessage = SR.GetString(SR.FileNameCannotContainALeadingPeriod, CultureInfo.CurrentUICulture); + } + else if (Utilities.ContainsInvalidFileNameChars(newFileName)) + { + errorMessage = SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture); + } + else + { + string url = Path.GetDirectoryName(newFileName); + string oldUrl = Path.GetDirectoryName(this.Url); + + if (!NativeMethods.IsSamePath(oldUrl, url)) + { + errorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.SaveOfProjectFileOutsideCurrentDirectory, CultureInfo.CurrentUICulture), this.ProjectFolder); + } + } + } + } + if (errorMessage.Length > 0) + { + // If it is not called from an automation method show a dialog box. + if (!Utilities.IsInAutomationFunction(this.Site)) + { + string title = null; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + VsShellUtilities.ShowMessageBox(this.Site, title, errorMessage, icon, buttons, defaultButton); + return VSConstants.OLE_E_PROMPTSAVECANCELLED; + } + + throw new InvalidOperationException(errorMessage); + } + + string oldName = this.filename; + + IVsSolution solution = this.Site.GetService(typeof(IVsSolution)) as IVsSolution; + Debug.Assert(solution != null, "Could not retrieve the solution form the service provider"); + if (solution == null) + { + throw new InvalidOperationException(); + } + + int canRenameContinue = 0; + ErrorHandler.ThrowOnFailure(solution.QueryRenameProject(this, this.filename, newFileName, 0, out canRenameContinue)); + + if (canRenameContinue == 0) + { + return VSConstants.OLE_E_PROMPTSAVECANCELLED; + } + + SuspendFileChanges fileChanges = new SuspendFileChanges(this.Site, oldName); + fileChanges.Suspend(); + try + { + // Save the project file and project file related properties. + this.SaveMSBuildProjectFileAs(newFileName); + + this.SetProjectFileDirty(false); + + + // TODO: If source control is enabled check out the project file. + + //Redraw. + this.OnPropertyChanged(this, (int)__VSHPROPID.VSHPROPID_Caption, 0); + + ErrorHandler.ThrowOnFailure(solution.OnAfterRenameProject(this, oldName, this.filename, 0)); + + IVsUIShell shell = this.Site.GetService(typeof(SVsUIShell)) as IVsUIShell; + Debug.Assert(shell != null, "Could not get the ui shell from the project"); + if (shell == null) + { + throw new InvalidOperationException(); + } + ErrorHandler.ThrowOnFailure(shell.RefreshPropertyBrowser(0)); + } + finally + { + fileChanges.Resume(); + } + + return VSConstants.S_OK; + } + + /// + /// Saves project file related information to the new file name. It also calls msbuild API to save the project file. + /// It is called by the SaveAs method and the SetEditLabel before the project file rename related events are triggered. + /// An implementer can override this method to provide specialized semantics on how the project file is renamed in the msbuild file. + /// + /// The new full path of the project file + protected virtual void SaveMSBuildProjectFileAs(string newFileName) + { + Debug.Assert(!String.IsNullOrEmpty(newFileName), "Cannot save project file for an empty or null file name"); + + this.buildProject.FullPath = newFileName; + + this.filename = newFileName; + + string newFileNameWithoutExtension = Path.GetFileNameWithoutExtension(newFileName); + + // Refresh solution explorer + this.SetProjectProperty(ProjectFileConstants.Name, newFileNameWithoutExtension); + + // Saves the project file on disk. + this.buildProject.Save(newFileName); + + } + + /// + /// Adds a file to the msbuild project. + /// + /// The file to be added. + /// A Projectelement describing the newly added file. + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ToMs")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")] + protected virtual ProjectElement AddFileToMsBuild(string file) + { + ProjectElement newItem; + + string itemPath = PackageUtilities.MakeRelativeIfRooted(file, this.BaseURI); + Debug.Assert(!Path.IsPathRooted(itemPath), "Cannot add item with full path."); + + if (this.IsCodeFile(itemPath)) + { + newItem = this.CreateMsBuildFileItem(itemPath, ProjectFileConstants.Compile); + newItem.SetMetadata(ProjectFileConstants.SubType, ProjectFileAttributeValue.Code); + } + else if (this.IsEmbeddedResource(itemPath)) + { + newItem = this.CreateMsBuildFileItem(itemPath, ProjectFileConstants.EmbeddedResource); + } + else + { + newItem = this.CreateMsBuildFileItem(itemPath, ProjectFileConstants.Content); + newItem.SetMetadata(ProjectFileConstants.SubType, ProjectFileConstants.Content); + } + + return newItem; + } + + /// + /// Adds a folder to the msbuild project. + /// + /// The folder to be added. + /// A Projectelement describing the newly added folder. + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "ToMs")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")] + protected virtual ProjectElement AddFolderToMsBuild(string folder) + { + ProjectElement newItem; + + string itemPath = PackageUtilities.MakeRelativeIfRooted(folder, this.BaseURI); + Debug.Assert(!Path.IsPathRooted(itemPath), "Cannot add item with full path."); + + newItem = this.CreateMsBuildFileItem(itemPath, ProjectFileConstants.Folder); + + return newItem; + } + + /// + /// Determines whether an item can be owerwritten in the hierarchy. + /// + /// The orginal filname. + /// The computed new file name, that will be copied to the project directory or into the folder . + /// S_OK for success, or an error message + protected virtual int CanOverwriteExistingItem(string originalFileName, string computedNewFileName) + { + if (String.IsNullOrEmpty(originalFileName) || String.IsNullOrEmpty(computedNewFileName)) + { + return VSConstants.E_INVALIDARG; + } + + string message = String.Empty; + string title = String.Empty; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + + // If the document is open then return error message. + IVsUIHierarchy hier; + IVsWindowFrame windowFrame; + uint itemid = VSConstants.VSITEMID_NIL; + + bool isOpen = VsShellUtilities.IsDocumentOpen(this.Site, computedNewFileName, Guid.Empty, out hier, out itemid, out windowFrame); + + if (isOpen) + { + message = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.CannotAddFileThatIsOpenInEditor, CultureInfo.CurrentUICulture), Path.GetFileName(computedNewFileName)); + VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton); + return VSConstants.E_ABORT; + } + + + // File already exists in project... message box + message = SR.GetString(SR.FileAlreadyInProject, CultureInfo.CurrentUICulture); + icon = OLEMSGICON.OLEMSGICON_QUERY; + buttons = OLEMSGBUTTON.OLEMSGBUTTON_YESNO; + int msgboxResult = VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton); + if (msgboxResult != NativeMethods.IDYES) + { + return (int)OleConstants.OLECMDERR_E_CANCELED; + } + + return VSConstants.S_OK; + } + + /// + /// Handle owerwriting of an existing item in the hierarchy. + /// + /// The node that exists. + protected virtual void OverwriteExistingItem(HierarchyNode existingNode) + { + + } + + /// + /// Adds a new file node to the hierarchy. + /// + /// The parent of the new fileNode + /// The file name + protected virtual void AddNewFileNodeToHierarchy(HierarchyNode parentNode, string fileName) + { + if (parentNode == null) + { + throw new ArgumentNullException("parentNode"); + } + + HierarchyNode child; + + // In the case of subitem, we want to create dependent file node + // and set the DependentUpon property + if (this.canFileNodesHaveChilds && (parentNode is FileNode || parentNode is DependentFileNode)) + { + child = this.CreateDependentFileNode(fileName); + child.ItemNode.SetMetadata(ProjectFileConstants.DependentUpon, parentNode.ItemNode.GetMetadata(ProjectFileConstants.Include)); + + // Make sure to set the HasNameRelation flag on the dependent node if it is related to the parent by name + if (!child.HasParentNodeNameRelation && string.Compare(child.GetRelationalName(), parentNode.GetRelationalName(), StringComparison.OrdinalIgnoreCase) == 0) + { + child.HasParentNodeNameRelation = true; + } + } + else + { + //Create and add new filenode to the project + child = this.CreateFileNode(fileName); + } + + parentNode.AddChild(child); + + // TODO : Revisit the VSADDFILEFLAGS here. Can it be a nested project? + this.tracker.OnItemAdded(fileName, VSADDFILEFLAGS.VSADDFILEFLAGS_NoFlags); + } + + /// + /// Defines whther the current mode of the project is in a supress command mode. + /// + /// + protected internal virtual bool IsCurrentStateASuppressCommandsMode() + { + if (VsShellUtilities.IsSolutionBuilding(this.Site)) + { + return true; + } + + DBGMODE dbgMode = VsShellUtilities.GetDebugMode(this.Site) & ~DBGMODE.DBGMODE_EncMask; + if (dbgMode == DBGMODE.DBGMODE_Run || dbgMode == DBGMODE.DBGMODE_Break) + { + return true; + } + + return false; + + } + + + /// + /// This is the list of output groups that the configuration object should + /// provide. + /// The first string is the name of the group. + /// The second string is the target name (MSBuild) for that group. + /// + /// To add/remove OutputGroups, simply override this method and edit the list. + /// + /// To get nice display names and description for your groups, override: + /// - GetOutputGroupDisplayName + /// - GetOutputGroupDescription + /// + /// List of output group name and corresponding MSBuild target + protected internal virtual IList> GetOutputGroupNames() + { + return new List>(outputGroupNames); + } + + /// + /// Get the display name of the given output group. + /// + /// Canonical name of the output group + /// Display name + protected internal virtual string GetOutputGroupDisplayName(string canonicalName) + { + string result = SR.GetString(String.Format(CultureInfo.InvariantCulture, "Output{0}", canonicalName), CultureInfo.CurrentUICulture); + if (String.IsNullOrEmpty(result)) + result = canonicalName; + return result; + } + + /// + /// Get the description of the given output group. + /// + /// Canonical name of the output group + /// Description + protected internal virtual string GetOutputGroupDescription(string canonicalName) + { + string result = SR.GetString(String.Format(CultureInfo.InvariantCulture, "Output{0}Description", canonicalName), CultureInfo.CurrentUICulture); + if (String.IsNullOrEmpty(result)) + result = canonicalName; + return result; + } + + /// + /// Set the configuration in MSBuild. + /// This does not get persisted and is used to evaluate msbuild conditions + /// which are based on the $(Configuration) property. + /// + protected internal virtual void SetCurrentConfiguration() + { + if (this.BuildInProgress) + { + // we are building so this should already be the current configuration + return; + } + + // Can't ask for the active config until the project is opened, so do nothing in that scenario + if (!this.projectOpened) + return; + + EnvDTE.Project automationObject = this.GetAutomationObject() as EnvDTE.Project; + + this.SetConfiguration(Utilities.GetActiveConfigurationName(automationObject)); + } + + /// + /// Set the configuration property in MSBuild. + /// This does not get persisted and is used to evaluate msbuild conditions + /// which are based on the $(Configuration) property. + /// + /// Configuration name + protected internal virtual void SetConfiguration(string config) + { + if (config == null) + { + throw new ArgumentNullException("config"); + } + + // Can't ask for the active config until the project is opened, so do nothing in that scenario + if (!projectOpened) + return; + + // We cannot change properties during the build so if the config + // we want to se is the current, we do nothing otherwise we fail. + if (this.BuildInProgress) + { + EnvDTE.Project automationObject = this.GetAutomationObject() as EnvDTE.Project; + string currentConfigName = Utilities.GetActiveConfigurationName(automationObject); + bool configsAreEqual = String.Compare(currentConfigName, config, StringComparison.OrdinalIgnoreCase) == 0; + + if (configsAreEqual) + { + return; + } + throw new InvalidOperationException(); + } + + bool propertiesChanged = this.buildProject.SetGlobalProperty(ProjectFileConstants.Configuration, config); + if (this.currentConfig == null || propertiesChanged) + { + this.currentConfig = this.buildProject.CreateProjectInstance(); + } + } + + /// + /// Loads reference items from the project file into the hierarchy. + /// + protected internal virtual void ProcessReferences() + { + IReferenceContainer container = GetReferenceContainer(); + if (null == container) + { + // Process References + ReferenceContainerNode referencesFolder = CreateReferenceContainerNode(); + if (null == referencesFolder) + { + // This project type does not support references or there is a problem + // creating the reference container node. + // In both cases there is no point to try to process references, so exit. + return; + } + this.AddChild(referencesFolder); + container = referencesFolder; + } + + // Load the referernces. + container.LoadReferencesFromBuildProject(buildProject); + } + + /// + /// Loads folders from the project file into the hierarchy. + /// + protected internal virtual void ProcessFolders() + { + // Process Folders (useful to persist empty folder) + foreach (MSBuild.ProjectItem folder in this.buildProject.GetItems(ProjectFileConstants.Folder)) + { + string strPath = folder.EvaluatedInclude; + + // We do not need any special logic for assuring that a folder is only added once to the ui hierarchy. + // The below method will only add once the folder to the ui hierarchy + this.CreateFolderNodes(strPath); + } + } + + /// + /// Loads file items from the project file into the hierarchy. + /// + protected internal virtual void ProcessFiles() + { + List subitemsKeys = new List(); + Dictionary subitems = new Dictionary(); + + // Define a set for our build items. The value does not really matter here. + Dictionary items = new Dictionary(); + + // Process Files + foreach (MSBuild.ProjectItem item in this.buildProject.Items) + { + // Ignore the item if it is a reference or folder + if (this.FilterItemTypeToBeAddedToHierarchy(item.ItemType)) + continue; + + // MSBuilds tasks/targets can create items (such as object files), + // such items are not part of the project per say, and should not be displayed. + // so ignore those items. + if (!this.IsItemTypeFileType(item.ItemType)) + continue; + + // If the item is already contained do nothing. + // TODO: possibly report in the error list that the the item is already contained in the project file similar to Language projects. + if (items.ContainsKey(item.EvaluatedInclude.ToUpperInvariant())) + continue; + + // Make sure that we do not want to add the item, dependent, or independent twice to the ui hierarchy + items.Add(item.EvaluatedInclude.ToUpperInvariant(), item); + + string dependentOf = item.GetMetadataValue(ProjectFileConstants.DependentUpon); + + if (!this.CanFileNodesHaveChilds || String.IsNullOrEmpty(dependentOf)) + { + AddIndependentFileNode(item); + } + else + { + // We will process dependent items later. + // Note that we use 2 lists as we want to remove elements from + // the collection as we loop through it + subitemsKeys.Add(item.EvaluatedInclude); + subitems.Add(item.EvaluatedInclude, item); + } + } + + // Now process the dependent items. + if (this.CanFileNodesHaveChilds) + { + ProcessDependentFileNodes(subitemsKeys, subitems); + } + + } + + /// + /// Processes dependent filenodes from list of subitems. Multi level supported, but not circular dependencies. + /// + /// List of sub item keys + /// + protected internal virtual void ProcessDependentFileNodes(IList subitemsKeys, Dictionary subitems) + { + if (subitemsKeys == null || subitems == null) + { + return; + } + + foreach (string key in subitemsKeys) + { + // A previous pass could have removed the key so make sure it still needs to be added + if (!subitems.ContainsKey(key)) + continue; + + AddDependentFileNode(subitems, key); + } + } + + /// + /// For flavored projects which implement IPersistXMLFragment, load the information now + /// + protected internal virtual void LoadNonBuildInformation() + { + IPersistXMLFragment outerHierarchy = HierarchyNode.GetOuterHierarchy(this) as IPersistXMLFragment; + if (outerHierarchy != null) + { + this.LoadXmlFragment(outerHierarchy, null); + } + } + + /// + /// Used to sort nodes in the hierarchy. + /// + protected internal virtual int CompareNodes(HierarchyNode node1, HierarchyNode node2) + { + Debug.Assert(node1 != null && node2 != null); + if (node1 == null) + { + throw new ArgumentNullException("node1"); + } + + if (node2 == null) + { + throw new ArgumentNullException("node2"); + } + + if (node1.SortPriority == node2.SortPriority) + { + return String.Compare(node2.Caption, node1.Caption, true, CultureInfo.CurrentCulture); + } + else + { + return node2.SortPriority - node1.SortPriority; + } + } + + /// + /// Handles global properties related to configuration and platform changes invoked by a change in the active configuration. + /// + /// The sender of the event. + /// The event args + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2109:ReviewVisibleEventHandlers", + Justification = "This method will give the opportunity to update global properties based on active configuration change. " + + "There is no security threat that could otherwise not be reached by listening to configuration chnage events.")] + protected virtual void OnHandleConfigurationRelatedGlobalProperties(object sender, ActiveConfigurationChangedEventArgs eventArgs) + { + Debug.Assert(eventArgs != null, "Wrong hierarchy passed as event arg for the configuration change listener."); + + // If (eventArgs.Hierarchy == NULL) then we received this event because the solution configuration + // was changed. + // If it is not null we got the event because a project in teh configuration manager has changed its active configuration. + // We care only about our project in the default implementation. + if (eventArgs == null || eventArgs.Hierarchy == null || !Utilities.IsSameComObject(eventArgs.Hierarchy, this)) + { + return; + } + + string name, platform; + if (!Utilities.TryGetActiveConfigurationAndPlatform(this.Site, this, out name, out platform)) + { + throw new InvalidOperationException(); + } + + this.buildProject.SetGlobalProperty(GlobalProperty.Configuration.ToString(), name); + + // If the platform is "Any CPU" then it should be set to AnyCPU, since that is the property value in MsBuild terms. + if (String.Compare(platform, ConfigProvider.AnyCPUPlatform, StringComparison.Ordinal) == 0) + { + platform = ProjectFileValues.AnyCPU; + } + + this.buildProject.SetGlobalProperty(GlobalProperty.Platform.ToString(), platform); + } + + /// + /// Flush any remaining content from build logger. + /// This method is called as part of the callback method passed to the buildsubmission during async build + /// so that results can be printed the the build is fisinshed. + /// + protected virtual void FlushBuildLoggerContent() + { + } + #endregion + + #region non-virtual methods + + /// + /// Suspends MSBuild + /// + public void SuspendMSBuild() + { + this.suspendMSBuildCounter++; + } + + /// + /// Resumes MSBuild. + /// + public void ResumeMSBuild(string config, IVsOutputWindowPane output, string target) + { + this.suspendMSBuildCounter--; + + if (this.suspendMSBuildCounter == 0 && this.invokeMSBuildWhenResumed) + { + try + { + this.Build(config, output, target); + } + finally + { + this.invokeMSBuildWhenResumed = false; + } + } + } + + /// + /// Resumes MSBuild. + /// + public void ResumeMSBuild(string config, string target) + { + this.ResumeMSBuild(config, null, target); + } + + /// + /// Resumes MSBuild. + /// + public void ResumeMSBuild(string target) + { + this.ResumeMSBuild(String.Empty, null, target); + } + + /// + /// Calls MSBuild if it is not suspended. If it is suspended then it will remember to call when msbuild is resumed. + /// + public MSBuildResult CallMSBuild(string config, IVsOutputWindowPane output, string target) + { + if (this.suspendMSBuildCounter > 0) + { + // remember to invoke MSBuild + this.invokeMSBuildWhenResumed = true; + return MSBuildResult.Suspended; + } + else + { + return this.Build(config, output, target); + } + } + + /// + /// Overloaded method. Calls MSBuild if it is not suspended. Does not log on the outputwindow. If it is suspended then it will remeber to call when msbuild is resumed. + /// + public MSBuildResult CallMSBuild(string config, string target) + { + return this.CallMSBuild(config, null, target); + } + + /// + /// Calls MSBuild if it is not suspended. Does not log and uses current configuration. If it is suspended then it will remeber to call when msbuild is resumed. + /// + public MSBuildResult CallMSBuild(string target) + { + return this.CallMSBuild(String.Empty, null, target); + } + + /// + /// Calls MSBuild if it is not suspended. Uses current configuration. If it is suspended then it will remeber to call when msbuild is resumed. + /// + public MSBuildResult CallMSBuild(string target, IVsOutputWindowPane output) + { + return this.CallMSBuild(String.Empty, output, target); + } + + /// + /// Overloaded method to invoke MSBuild + /// + public MSBuildResult Build(string config, IVsOutputWindowPane output, string target) + { + return this.Build(0, config, output, target); + } + + /// + /// Overloaded method to invoke MSBuild. Does not log build results to the output window pane. + /// + public MSBuildResult Build(string config, string target) + { + return this.Build(0, config, null, target); + } + + /// + /// Overloaded method. Invokes MSBuild using the default configuration and does without logging on the output window pane. + /// + public MSBuildResult Build(string target) + { + return this.Build(0, String.Empty, null, target); + } + + /// + /// Overloaded method. Invokes MSBuild using the default configuration. + /// + public MSBuildResult Build(string target, IVsOutputWindowPane output) + { + return this.Build(0, String.Empty, output, target); + } + + /// + /// Get the output path for a specific configuration name + /// + /// name of configuration + /// Output path + public string GetOutputPath(string config) + { + this.SetConfiguration(config); + return this.GetOutputPath(this.currentConfig); + } + + /// + /// Get value of Project property + /// + /// Name of Property to retrieve + /// Value of property + public string GetProjectProperty(string propertyName) + { + return this.GetProjectProperty(propertyName, true); + } + + /// + /// Set dirty state of project + /// + /// boolean value indicating dirty state + public void SetProjectFileDirty(bool value) + { + this.isDirty = value; + if (this.isDirty) + { + this.lastModifiedTime = DateTime.Now; + this.buildIsPrepared = false; + } + } + + /// + /// Get output assembly for a specific configuration name + /// + /// Name of configuration + /// Name of output assembly + public string GetOutputAssembly(string config) + { + ProjectOptions options = this.GetProjectOptions(config); + + return options.OutputAssembly; + } + + /// + /// Get Node from ItemID. + /// + /// ItemID for the requested node + /// Node if found + public HierarchyNode NodeFromItemId(uint itemId) + { + if (VSConstants.VSITEMID_ROOT == itemId) + { + return this; + } + else if (VSConstants.VSITEMID_NIL == itemId) + { + return null; + } + else if (VSConstants.VSITEMID_SELECTION == itemId) + { + throw new NotImplementedException(); + } + + return (HierarchyNode)this.ItemIdMap[itemId]; + } + + /// + /// This method return new project element, and add new MSBuild item to the project/build hierarchy + /// + /// file name + /// MSBuild item type + /// new project element + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")] + public ProjectElement CreateMsBuildFileItem(string file, string itemType) + { + return new ProjectElement(this, file, itemType); + } + + /// + /// This method returns new project element based on existing MSBuild item. It does not modify/add project/build hierarchy at all. + /// + /// MSBuild item instance + /// wrapping project element + public ProjectElement GetProjectElement(MSBuild.ProjectItem item) + { + return new ProjectElement(this, item, false); + } + + /// + /// Create FolderNode from Path + /// + /// Path to folder + /// FolderNode created that can be added to the hierarchy + protected internal FolderNode CreateFolderNode(string path) + { + ProjectElement item = this.AddFolderToMsBuild(path); + FolderNode folderNode = CreateFolderNode(path, item); + return folderNode; + } + + /// + /// Verify if the file can be written to. + /// Return false if the file is read only and/or not checked out + /// and the user did not give permission to change it. + /// Note that exact behavior can also be affected based on the SCC + /// settings under Tools->Options. + /// + internal bool QueryEditProjectFile(bool suppressUI) + { + bool result = true; + if (this.site == null) + { + // We're already zombied. Better return FALSE. + result = false; + } + else if (this.disableQueryEdit) + { + return true; + } + else + { + IVsQueryEditQuerySave2 queryEditQuerySave = this.GetService(typeof(SVsQueryEditQuerySave)) as IVsQueryEditQuerySave2; + if (queryEditQuerySave != null) + { // Project path dependends on server/client project + string path = this.filename; + + tagVSQueryEditFlags qef = tagVSQueryEditFlags.QEF_AllowInMemoryEdits; + if (suppressUI) + qef |= tagVSQueryEditFlags.QEF_SilentMode; + + // If we are debugging, we want to prevent our project from being reloaded. To + // do this, we pass the QEF_NoReload flag + if (!Utilities.IsVisualStudioInDesignMode(this.Site)) + qef |= tagVSQueryEditFlags.QEF_NoReload; + + uint verdict; + uint moreInfo; + string[] files = new string[1]; + files[0] = path; + uint[] flags = new uint[1]; + VSQEQS_FILE_ATTRIBUTE_DATA[] attributes = new VSQEQS_FILE_ATTRIBUTE_DATA[1]; + int hr = queryEditQuerySave.QueryEditFiles( + (uint)qef, + 1, // 1 file + files, // array of files + flags, // no per file flags + attributes, // no per file file attributes + out verdict, + out moreInfo /* ignore additional results */); + + tagVSQueryEditResult qer = (tagVSQueryEditResult)verdict; + if (ErrorHandler.Failed(hr) || (qer != tagVSQueryEditResult.QER_EditOK)) + { + if (!suppressUI && !Utilities.IsInAutomationFunction(this.Site)) + { + string message = SR.GetString(SR.CancelQueryEdit, path); + string title = string.Empty; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton); + } + result = false; + } + } + } + return result; + } + + /// + /// Checks whether a hierarchy is a nested project. + /// + /// + /// + internal NestedProjectNode GetNestedProjectForHierarchy(IVsHierarchy hierarchy) + { + IVsProject3 project = hierarchy as IVsProject3; + + if (project != null) + { + string mkDocument = String.Empty; + ErrorHandler.ThrowOnFailure(project.GetMkDocument(VSConstants.VSITEMID_ROOT, out mkDocument)); + + if (!String.IsNullOrEmpty(mkDocument)) + { + HierarchyNode node = this.FindChild(mkDocument); + + return node as NestedProjectNode; + } + } + + return null; + } + + /// + /// Given a node determines what is the directory that can accept files. + /// If the node is a FoldeNode than it is the Url of the Folder. + /// If the node is a ProjectNode it is the project folder. + /// Otherwise (such as FileNode subitem) it delegate the resolution to the parent node. + /// + internal string GetBaseDirectoryForAddingFiles(HierarchyNode nodeToAddFile) + { + string baseDir = String.Empty; + + if (nodeToAddFile is FolderNode) + { + baseDir = nodeToAddFile.Url; + } + else if (nodeToAddFile is ProjectNode) + { + baseDir = this.ProjectFolder; + } + else if (nodeToAddFile != null) + { + baseDir = GetBaseDirectoryForAddingFiles(nodeToAddFile.Parent); + } + + return baseDir; + } + + /// + /// For internal use only. + /// This creates a copy of an existing configuration and add it to the project. + /// Caller should change the condition on the PropertyGroup. + /// If derived class want to accomplish this, they should call ConfigProvider.AddCfgsOfCfgName() + /// It is expected that in the future MSBuild will have support for this so we don't have to + /// do it manually. + /// + /// PropertyGroup to clone + /// + internal MSBuildConstruction.ProjectPropertyGroupElement ClonePropertyGroup(MSBuildConstruction.ProjectPropertyGroupElement group) + { + // Create a new (empty) PropertyGroup + MSBuildConstruction.ProjectPropertyGroupElement newPropertyGroup = this.buildProject.Xml.AddPropertyGroup(); + + // Now copy everything from the group we are trying to clone to the group we are creating + if (!String.IsNullOrEmpty(group.Condition)) + newPropertyGroup.Condition = group.Condition; + foreach (MSBuildConstruction.ProjectPropertyElement prop in group.Properties) + { + MSBuildConstruction.ProjectPropertyElement newProperty = newPropertyGroup.AddProperty(prop.Name, prop.Value); + if (!String.IsNullOrEmpty(prop.Condition)) + newProperty.Condition = prop.Condition; + } + + return newPropertyGroup; + } + + /// + /// Get the project extensions + /// + /// + internal MSBuildConstruction.ProjectExtensionsElement GetProjectExtensions() + { + var extensionsElement = this.buildProject.Xml.ChildrenReversed.OfType().FirstOrDefault(); + + if (extensionsElement == null) + { + extensionsElement = this.buildProject.Xml.CreateProjectExtensionsElement(); + this.buildProject.Xml.AppendChild(extensionsElement); + } + + return extensionsElement; + } + + /// + /// Set the xmlText as a project extension element with the id passed. + /// + /// The id of the project extension element. + /// The value to set for a project extension. + internal void SetProjectExtensions(string id, string xmlText) + { + MSBuildConstruction.ProjectExtensionsElement element = this.GetProjectExtensions(); + + // If it doesn't already have a value and we're asked to set it to + // nothing, don't do anything. Same as old OM. Keeps project neat. + if (element == null) + { + if (xmlText.Length == 0) + { + return; + } + + element = this.buildProject.Xml.CreateProjectExtensionsElement(); + this.buildProject.Xml.AppendChild(element); + } + + element[id] = xmlText; + } + + /// + /// Register the project with the Scc manager. + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")] + protected void RegisterSccProject() + { + + if (this.IsSccDisabled || this.isRegisteredWithScc || String.IsNullOrEmpty(this.sccProjectName)) + { + return; + } + + IVsSccManager2 sccManager = this.Site.GetService(typeof(SVsSccManager)) as IVsSccManager2; + + if (sccManager != null) + { + ErrorHandler.ThrowOnFailure(sccManager.RegisterSccProject(this, this.sccProjectName, this.sccAuxPath, this.sccLocalPath, this.sccProvider)); + + this.isRegisteredWithScc = true; + } + } + + /// + /// Unregisters us from the SCC manager + /// + [SuppressMessage("Microsoft.Naming", "CA1702:CompoundWordsShouldBeCasedCorrectly", MessageId = "UnRegister")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Un")] + protected void UnRegisterProject() + { + if (this.IsSccDisabled || !this.isRegisteredWithScc) + { + return; + } + + IVsSccManager2 sccManager = this.Site.GetService(typeof(SVsSccManager)) as IVsSccManager2; + + if (sccManager != null) + { + ErrorHandler.ThrowOnFailure(sccManager.UnregisterSccProject(this)); + this.isRegisteredWithScc = false; + } + } + + /// + /// Get the CATID corresponding to the specified type. + /// + /// Type of the object for which you want the CATID + /// CATID + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CATID")] + protected internal Guid GetCATIDForType(Type type) + { + if (type == null) + throw new ArgumentNullException("type"); + + if (catidMapping.ContainsKey(type)) + return catidMapping[type]; + // If you get here and you want your object to be extensible, then add a call to AddCATIDMapping() in your project constructor + return Guid.Empty; + } + + /// + /// This is used to specify a CATID corresponding to a BrowseObject or an ExtObject. + /// The CATID can be any GUID you choose. For types which are your owns, you could use + /// their type GUID, while for other types (such as those provided in the MPF) you should + /// provide a different GUID. + /// + /// Type of the extensible object + /// GUID that extender can use to uniquely identify your object type + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "catid")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CATID")] + protected void AddCATIDMapping(Type type, Guid catid) + { + catidMapping.Add(type, catid); + } + + /// + /// Initialize an object with an XML fragment. + /// + /// Object that support being initialized with an XML fragment + /// Name of the configuration being initialized, null if it is the project + protected internal void LoadXmlFragment(IPersistXMLFragment persistXmlFragment, string configName) + { + if (persistXmlFragment == null) + { + throw new ArgumentNullException("persistXmlFragment"); + } + + if (xmlFragments == null) + { + // Retrieve the xml fragments from MSBuild + xmlFragments = new XmlDocument(); + + string fragments = GetProjectExtensions()[ProjectFileConstants.VisualStudio]; + fragments = String.Format(CultureInfo.InvariantCulture, "{0}", fragments); + xmlFragments.LoadXml(fragments); + } + + // We need to loop through all the flavors + string flavorsGuid; + ErrorHandler.ThrowOnFailure(((IVsAggregatableProject)this).GetAggregateProjectTypeGuids(out flavorsGuid)); + foreach (Guid flavor in Utilities.GuidsArrayFromSemicolonDelimitedStringOfGuids(flavorsGuid)) + { + // Look for a matching fragment + string flavorGuidString = flavor.ToString("B"); + string fragment = null; + XmlNode node = null; + foreach (XmlNode child in xmlFragments.FirstChild.ChildNodes) + { + if (child.Attributes.Count > 0) + { + string guid = String.Empty; + string configuration = String.Empty; + if (child.Attributes[ProjectFileConstants.Guid] != null) + guid = child.Attributes[ProjectFileConstants.Guid].Value; + if (child.Attributes[ProjectFileConstants.Configuration] != null) + configuration = child.Attributes[ProjectFileConstants.Configuration].Value; + + if (String.Compare(child.Name, ProjectFileConstants.FlavorProperties, StringComparison.OrdinalIgnoreCase) == 0 + && String.Compare(guid, flavorGuidString, StringComparison.OrdinalIgnoreCase) == 0 + && ((String.IsNullOrEmpty(configName) && String.IsNullOrEmpty(configuration)) + || (String.Compare(configuration, configName, StringComparison.OrdinalIgnoreCase) == 0))) + { + // we found the matching fragment + fragment = child.InnerXml; + node = child; + break; + } + } + } + + Guid flavorGuid = flavor; + if (String.IsNullOrEmpty(fragment)) + { + // the fragment was not found so init with default values + ErrorHandler.ThrowOnFailure(persistXmlFragment.InitNew(ref flavorGuid, (uint)_PersistStorageType.PST_PROJECT_FILE)); + // While we don't yet support user files, our flavors might, so we will store that in the project file until then + // TODO: Refactor this code when we support user files + ErrorHandler.ThrowOnFailure(persistXmlFragment.InitNew(ref flavorGuid, (uint)_PersistStorageType.PST_USER_FILE)); + } + else + { + ErrorHandler.ThrowOnFailure(persistXmlFragment.Load(ref flavorGuid, (uint)_PersistStorageType.PST_PROJECT_FILE, fragment)); + // While we don't yet support user files, our flavors might, so we will store that in the project file until then + // TODO: Refactor this code when we support user files + if (node.NextSibling != null && node.NextSibling.Attributes[ProjectFileConstants.User] != null) + ErrorHandler.ThrowOnFailure(persistXmlFragment.Load(ref flavorGuid, (uint)_PersistStorageType.PST_USER_FILE, node.NextSibling.InnerXml)); + } + } + } + + /// + /// Retrieve all XML fragments that need to be saved from the flavors and store the information in msbuild. + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "XML")] + protected void PersistXMLFragments() + { + if (this.IsFlavorDirty() != 0) + { + XmlDocument doc = new XmlDocument(); + XmlElement root = doc.CreateElement("ROOT"); + + // We will need the list of configuration inside the loop, so get it before entering the loop + uint[] count = new uint[1]; + IVsCfg[] configs = null; + int hr = this.ConfigProvider.GetCfgs(0, null, count, null); + if (ErrorHandler.Succeeded(hr) && count[0] > 0) + { + configs = new IVsCfg[count[0]]; + hr = this.ConfigProvider.GetCfgs((uint)configs.Length, configs, count, null); + if (ErrorHandler.Failed(hr)) + count[0] = 0; + } + if (count[0] == 0) + configs = new IVsCfg[0]; + + // We need to loop through all the flavors + string flavorsGuid; + ErrorHandler.ThrowOnFailure(((IVsAggregatableProject)this).GetAggregateProjectTypeGuids(out flavorsGuid)); + foreach (Guid flavor in Utilities.GuidsArrayFromSemicolonDelimitedStringOfGuids(flavorsGuid)) + { + IPersistXMLFragment outerHierarchy = HierarchyNode.GetOuterHierarchy(this) as IPersistXMLFragment; + // First check the project + if (outerHierarchy != null) + { + // Retrieve the XML fragment + string fragment = string.Empty; + Guid flavorGuid = flavor; + ErrorHandler.ThrowOnFailure((outerHierarchy).Save(ref flavorGuid, (uint)_PersistStorageType.PST_PROJECT_FILE, out fragment, 1)); + if (!String.IsNullOrEmpty(fragment)) + { + // Add the fragment to our XML + WrapXmlFragment(doc, root, flavor, null, fragment); + } + // While we don't yet support user files, our flavors might, so we will store that in the project file until then + // TODO: Refactor this code when we support user files + fragment = String.Empty; + ErrorHandler.ThrowOnFailure((outerHierarchy).Save(ref flavorGuid, (uint)_PersistStorageType.PST_USER_FILE, out fragment, 1)); + if (!String.IsNullOrEmpty(fragment)) + { + // Add the fragment to our XML + XmlElement node = WrapXmlFragment(doc, root, flavor, null, fragment); + node.Attributes.Append(doc.CreateAttribute(ProjectFileConstants.User)); + } + } + + // Then look at the configurations + foreach (IVsCfg config in configs) + { + // Get the fragment for this flavor/config pair + string fragment; + ErrorHandler.ThrowOnFailure(((ProjectConfig)config).GetXmlFragment(flavor, _PersistStorageType.PST_PROJECT_FILE, out fragment)); + if (!String.IsNullOrEmpty(fragment)) + { + string configName; + ErrorHandler.ThrowOnFailure(config.get_DisplayName(out configName)); + WrapXmlFragment(doc, root, flavor, configName, fragment); + } + } + } + if (root.ChildNodes != null && root.ChildNodes.Count > 0) + { + // Save our XML (this is only the non-build information for each flavor) in msbuild + SetProjectExtensions(ProjectFileConstants.VisualStudio, root.InnerXml.ToString()); + } + } + } + + #endregion + + #region IVsGetCfgProvider Members + //================================================================================= + + public virtual int GetCfgProvider(out IVsCfgProvider p) + { + CCITracing.TraceCall(); + // Be sure to call the property here since that is doing a polymorhic ProjectConfig creation. + p = this.ConfigProvider; + return (p == null ? VSConstants.E_NOTIMPL : VSConstants.S_OK); + } + #endregion + + #region IPersist Members + + public int GetClassID(out Guid clsid) + { + clsid = this.ProjectGuid; + return VSConstants.S_OK; + } + #endregion + + #region IPersistFileFormat Members + + int IPersistFileFormat.GetClassID(out Guid clsid) + { + clsid = this.ProjectGuid; + return VSConstants.S_OK; + } + + public virtual int GetCurFile(out string name, out uint formatIndex) + { + name = this.filename; + formatIndex = 0; + return VSConstants.S_OK; + } + + public virtual int GetFormatList(out string formatlist) + { + formatlist = String.Empty; + return VSConstants.S_OK; + } + + public virtual int InitNew(uint formatIndex) + { + return VSConstants.S_OK; + } + + public virtual int IsDirty(out int isDirty) + { + isDirty = 0; + if (this.buildProject.Xml.HasUnsavedChanges || this.IsProjectFileDirty) + { + isDirty = 1; + return VSConstants.S_OK; + } + + isDirty = IsFlavorDirty(); + + return VSConstants.S_OK; + } + + protected int IsFlavorDirty() + { + int isDirty = 0; + // See if one of our flavor consider us dirty + IPersistXMLFragment outerHierarchy = HierarchyNode.GetOuterHierarchy(this) as IPersistXMLFragment; + if (outerHierarchy != null) + { + // First check the project + ErrorHandler.ThrowOnFailure(outerHierarchy.IsFragmentDirty((uint)_PersistStorageType.PST_PROJECT_FILE, out isDirty)); + // While we don't yet support user files, our flavors might, so we will store that in the project file until then + // TODO: Refactor this code when we support user files + if (isDirty == 0) + ErrorHandler.ThrowOnFailure(outerHierarchy.IsFragmentDirty((uint)_PersistStorageType.PST_USER_FILE, out isDirty)); + } + if (isDirty == 0) + { + // Then look at the configurations + uint[] count = new uint[1]; + int hr = this.ConfigProvider.GetCfgs(0, null, count, null); + if (ErrorHandler.Succeeded(hr) && count[0] > 0) + { + // We need to loop through the configurations + IVsCfg[] configs = new IVsCfg[count[0]]; + hr = this.ConfigProvider.GetCfgs((uint)configs.Length, configs, count, null); + Debug.Assert(ErrorHandler.Succeeded(hr), "failed to retrieve configurations"); + foreach (IVsCfg config in configs) + { + isDirty = ((ProjectConfig)config).IsFlavorDirty(_PersistStorageType.PST_PROJECT_FILE); + if (isDirty != 0) + break; + } + } + } + return isDirty; + } + + public virtual int Load(string fileName, uint mode, int readOnly) + { + this.filename = fileName; + this.Reload(); + return VSConstants.S_OK; + } + + public virtual int Save(string fileToBeSaved, int remember, uint formatIndex) + { + + // The file name can be null. Then try to use the Url. + string tempFileToBeSaved = fileToBeSaved; + if (String.IsNullOrEmpty(tempFileToBeSaved) && !String.IsNullOrEmpty(this.Url)) + { + tempFileToBeSaved = this.Url; + } + + if (String.IsNullOrEmpty(tempFileToBeSaved)) + { + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "fileToBeSaved"); + } + + bool setProjectFileDirtyAfterSave = false; + if (remember == 0) + { + setProjectFileDirtyAfterSave = this.IsProjectFileDirty; + } + + // Update the project with the latest flavor data (if needed) + PersistXMLFragments(); + + int result = VSConstants.S_OK; + bool saveAs = true; + if (NativeMethods.IsSamePath(tempFileToBeSaved, this.filename)) + { + saveAs = false; + } + if (!saveAs) + { + SuspendFileChanges fileChanges = new SuspendFileChanges(this.Site, this.filename); + fileChanges.Suspend(); + try + { + // Ensure the directory exist + string saveFolder = Path.GetDirectoryName(tempFileToBeSaved); + if (!Directory.Exists(saveFolder)) + Directory.CreateDirectory(saveFolder); + // Save the project + this.buildProject.Save(tempFileToBeSaved); + this.SetProjectFileDirty(false); + } + finally + { + fileChanges.Resume(); + } + } + else + { + result = this.SaveAs(tempFileToBeSaved); + if (result != VSConstants.OLE_E_PROMPTSAVECANCELLED) + { + ErrorHandler.ThrowOnFailure(result); + } + + } + + if (setProjectFileDirtyAfterSave) + { + this.SetProjectFileDirty(true); + } + + return result; + } + + public virtual int SaveCompleted(string filename) + { + // TODO: turn file watcher back on. + return VSConstants.S_OK; + } + #endregion + + #region IVsProject3 Members + + /// + /// Callback from the additem dialog. Deals with adding new and existing items + /// + public virtual int GetMkDocument(uint itemId, out string mkDoc) + { + mkDoc = null; + if (itemId == VSConstants.VSITEMID_SELECTION) + { + return VSConstants.E_UNEXPECTED; + } + + HierarchyNode n = this.NodeFromItemId(itemId); + if (n == null) + { + return VSConstants.E_INVALIDARG; + } + + mkDoc = n.GetMkDocument(); + + if (String.IsNullOrEmpty(mkDoc)) + { + return VSConstants.E_FAIL; + } + + return VSConstants.S_OK; + } + + + public virtual int AddItem(uint itemIdLoc, VSADDITEMOPERATION op, string itemName, uint filesToOpen, string[] files, IntPtr dlgOwner, VSADDRESULT[] result) + { + Guid empty = Guid.Empty; + + return AddItemWithSpecific(itemIdLoc, op, itemName, filesToOpen, files, dlgOwner, 0, ref empty, null, ref empty, result); + } + + /// + /// Creates new items in a project, adds existing files to a project, or causes Add Item wizards to be run + /// + /// + /// + /// + /// + /// Array of file names. + /// If dwAddItemOperation is VSADDITEMOP_CLONEFILE the first item in the array is the name of the file to clone. + /// If dwAddItemOperation is VSADDITEMOP_OPENDIRECTORY, the first item in the array is the directory to open. + /// If dwAddItemOperation is VSADDITEMOP_RUNWIZARD, the first item is the name of the wizard to run, + /// and the second item is the file name the user supplied (same as itemName). + /// + /// + /// + /// + /// + /// + /// S_OK if it succeeds + /// The result array is initalized to failure. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1506:AvoidExcessiveClassCoupling"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Maintainability", "CA1502:AvoidExcessiveComplexity")] + public virtual int AddItemWithSpecific(uint itemIdLoc, VSADDITEMOPERATION op, string itemName, uint filesToOpen, string[] files, IntPtr dlgOwner, uint editorFlags, ref Guid editorType, string physicalView, ref Guid logicalView, VSADDRESULT[] result) + { + if (files == null || result == null || files.Length == 0 || result.Length == 0) + { + return VSConstants.E_INVALIDARG; + } + + // Locate the node to be the container node for the file(s) being added + // only projectnode or foldernode and file nodes are valid container nodes + // We need to locate the parent since the item wizard expects the parent to be passed. + HierarchyNode n = this.NodeFromItemId(itemIdLoc); + if (n == null) + { + return VSConstants.E_INVALIDARG; + } + + while ((!(n is ProjectNode)) && (!(n is FolderNode)) && (!this.CanFileNodesHaveChilds || !(n is FileNode))) + { + n = n.Parent; + } + Debug.Assert(n != null, "We should at this point have either a ProjectNode or FolderNode or a FileNode as a container for the new filenodes"); + + // handle link and runwizard operations at this point + switch (op) + { + case VSADDITEMOPERATION.VSADDITEMOP_LINKTOFILE: + // we do not support this right now + throw new NotImplementedException("VSADDITEMOP_LINKTOFILE"); + + case VSADDITEMOPERATION.VSADDITEMOP_RUNWIZARD: + result[0] = this.RunWizard(n, itemName, files[0], dlgOwner); + return VSConstants.S_OK; + } + + string[] actualFiles = new string[files.Length]; + + + VSQUERYADDFILEFLAGS[] flags = this.GetQueryAddFileFlags(files); + + string baseDir = this.GetBaseDirectoryForAddingFiles(n); + // If we did not get a directory for node that is the parent of the item then fail. + if (String.IsNullOrEmpty(baseDir)) + { + return VSConstants.E_FAIL; + } + + // Pre-calculates some paths that we can use when calling CanAddItems + List filesToAdd = new List(); + for (int index = 0; index < files.Length; index++) + { + string newFileName = String.Empty; + + string file = files[index]; + + switch (op) + { + case VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE: + { + string fileName = Path.GetFileName(itemName); + newFileName = Path.Combine(baseDir, fileName); + } + break; + case VSADDITEMOPERATION.VSADDITEMOP_OPENFILE: + { + string fileName = Path.GetFileName(file); + newFileName = Path.Combine(baseDir, fileName); + } + break; + } + filesToAdd.Add(newFileName); + } + + // Ask tracker objects if we can add files + if (!this.tracker.CanAddItems(filesToAdd.ToArray(), flags)) + { + // We were not allowed to add the files + return VSConstants.E_FAIL; + } + + if (!this.ProjectMgr.QueryEditProjectFile(false)) + { + throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + + // Add the files to the hierarchy + int actualFilesAddedIndex = 0; + for (int index = 0; index < filesToAdd.Count; index++) + { + HierarchyNode child; + bool overwrite = false; + string newFileName = filesToAdd[index]; + + string file = files[index]; + result[0] = VSADDRESULT.ADDRESULT_Failure; + + child = this.FindChild(newFileName); + if (child != null) + { + // If the file to be added is an existing file part of the hierarchy then continue. + if (NativeMethods.IsSamePath(file, newFileName)) + { + result[0] = VSADDRESULT.ADDRESULT_Cancel; + continue; + } + + int canOverWriteExistingItem = this.CanOverwriteExistingItem(file, newFileName); + + if (canOverWriteExistingItem == (int)OleConstants.OLECMDERR_E_CANCELED) + { + result[0] = VSADDRESULT.ADDRESULT_Cancel; + return canOverWriteExistingItem; + } + else if (canOverWriteExistingItem == VSConstants.S_OK) + { + overwrite = true; + } + else + { + return canOverWriteExistingItem; + } + } + + // If the file to be added is not in the same path copy it. + if (NativeMethods.IsSamePath(file, newFileName) == false) + { + if (!overwrite && File.Exists(newFileName)) + { + string message = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.FileAlreadyExists, CultureInfo.CurrentUICulture), newFileName); + string title = string.Empty; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_QUERY; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_YESNO; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + int messageboxResult = VsShellUtilities.ShowMessageBox(this.Site, title, message, icon, buttons, defaultButton); + if (messageboxResult == NativeMethods.IDNO) + { + result[0] = VSADDRESULT.ADDRESULT_Cancel; + return (int)OleConstants.OLECMDERR_E_CANCELED; + } + } + + // Copy the file to the correct location. + // We will suppress the file change events to be triggered to this item, since we are going to copy over the existing file and thus we will trigger a file change event. + // We do not want the filechange event to ocur in this case, similar that we do not want a file change event to occur when saving a file. + IVsFileChangeEx fileChange = this.site.GetService(typeof(SVsFileChangeEx)) as IVsFileChangeEx; + if (fileChange == null) + { + throw new InvalidOperationException(); + } + + try + { + ErrorHandler.ThrowOnFailure(fileChange.IgnoreFile(VSConstants.VSCOOKIE_NIL, newFileName, 1)); + if (op == VSADDITEMOPERATION.VSADDITEMOP_CLONEFILE) + { + this.AddFileFromTemplate(file, newFileName); + } + else + { + PackageUtilities.CopyUrlToLocal(new Uri(file), newFileName); + } + } + finally + { + ErrorHandler.ThrowOnFailure(fileChange.IgnoreFile(VSConstants.VSCOOKIE_NIL, newFileName, 0)); + } + } + + if (overwrite) + { + this.OverwriteExistingItem(child); + } + else + { + //Add new filenode/dependentfilenode + this.AddNewFileNodeToHierarchy(n, newFileName); + } + + result[0] = VSADDRESULT.ADDRESULT_Success; + actualFiles[actualFilesAddedIndex++] = newFileName; + } + + // Notify listeners that items were appended. + if (actualFilesAddedIndex > 0) + n.OnItemsAppended(n); + + //Open files if this was requested through the editorFlags + bool openFiles = (editorFlags & (uint)__VSSPECIFICEDITORFLAGS.VSSPECIFICEDITOR_DoOpen) != 0; + if (openFiles && actualFiles.Length <= filesToOpen) + { + for (int i = 0; i < filesToOpen; i++) + { + if (!String.IsNullOrEmpty(actualFiles[i])) + { + string name = actualFiles[i]; + HierarchyNode child = this.FindChild(name); + Debug.Assert(child != null, "We should have been able to find the new element in the hierarchy"); + if (child != null) + { + IVsWindowFrame frame; + if (editorType == Guid.Empty) + { + Guid view = Guid.Empty; + ErrorHandler.ThrowOnFailure(this.OpenItem(child.ID, ref view, IntPtr.Zero, out frame)); + } + else + { + ErrorHandler.ThrowOnFailure(this.OpenItemWithSpecific(child.ID, editorFlags, ref editorType, physicalView, ref logicalView, IntPtr.Zero, out frame)); + } + + // Show the window frame in the UI and make it the active window + if (frame != null) + { + ErrorHandler.ThrowOnFailure(frame.Show()); + } + } + } + } + } + + return VSConstants.S_OK; + } + + /// + /// for now used by add folder. Called on the ROOT, as only the project should need + /// to implement this. + /// for folders, called with parent folder, blank extension and blank suggested root + /// + public virtual int GenerateUniqueItemName(uint itemIdLoc, string ext, string suggestedRoot, out string itemName) + { + string rootName = String.Empty; + string extToUse = String.Empty; + itemName = String.Empty; + + //force new items to have a number + int cb = 1; + bool found = false; + bool fFolderCase = false; + HierarchyNode parent = this.NodeFromItemId(itemIdLoc); + + if (!String.IsNullOrEmpty(ext)) + { + extToUse = ext.Trim(); + } + + if (!String.IsNullOrEmpty(suggestedRoot)) + { + suggestedRoot = suggestedRoot.Trim(); + } + + if (suggestedRoot == null || suggestedRoot.Length == 0) + { + // foldercase, we assume... + suggestedRoot = "NewFolder"; + fFolderCase = true; + } + + while (!found) + { + rootName = suggestedRoot; + if (cb > 0) + rootName += cb.ToString(CultureInfo.CurrentCulture); + + if (extToUse.Length > 0) + { + rootName += extToUse; + } + + cb++; + found = true; + for (HierarchyNode n = parent.FirstChild; n != null; n = n.NextSibling) + { + if (rootName == n.GetEditLabel()) + { + found = false; + break; + } + + //if parent is a folder, we need the whole url + string parentFolder = parent.Url; + if (parent is ProjectNode) + parentFolder = Path.GetDirectoryName(parent.Url); + + string checkFile = Path.Combine(parentFolder, rootName); + + if (fFolderCase) + { + if (Directory.Exists(checkFile)) + { + found = false; + break; + } + } + else + { + if (File.Exists(checkFile)) + { + found = false; + break; + } + } + } + } + + itemName = rootName; + return VSConstants.S_OK; + } + + + public virtual int GetItemContext(uint itemId, out Microsoft.VisualStudio.OLE.Interop.IServiceProvider psp) + { + CCITracing.TraceCall(); + psp = null; + HierarchyNode child = this.NodeFromItemId(itemId); + if (child != null) + { + psp = child.OleServiceProvider as IOleServiceProvider; + } + return VSConstants.S_OK; + } + + + public virtual int IsDocumentInProject(string mkDoc, out int found, VSDOCUMENTPRIORITY[] pri, out uint itemId) + { + CCITracing.TraceCall(); + if (pri != null && pri.Length >= 1) + { + pri[0] = VSDOCUMENTPRIORITY.DP_Unsupported; + } + found = 0; + itemId = 0; + + // If it is the project file just return. + if (NativeMethods.IsSamePath(mkDoc, this.GetMkDocument())) + { + found = 1; + itemId = VSConstants.VSITEMID_ROOT; + } + else + { + HierarchyNode child = this.FindChild(mkDoc); + if (child != null) + { + found = 1; + itemId = child.ID; + } + } + + if (found == 1) + { + if (pri != null && pri.Length >= 1) + { + pri[0] = VSDOCUMENTPRIORITY.DP_Standard; + } + } + + return VSConstants.S_OK; + + } + + + public virtual int OpenItem(uint itemId, ref Guid logicalView, IntPtr punkDocDataExisting, out IVsWindowFrame frame) + { + // Init output params + frame = null; + + HierarchyNode n = this.NodeFromItemId(itemId); + if (n == null) + { + throw new ArgumentException(SR.GetString(SR.ParameterMustBeAValidItemId, CultureInfo.CurrentUICulture), "itemId"); + } + + // Delegate to the document manager object that knows how to open the item + DocumentManager documentManager = n.GetDocumentManager(); + if (documentManager != null) + { + return documentManager.Open(ref logicalView, punkDocDataExisting, out frame, WindowFrameShowAction.DoNotShow); + } + + // This node does not have an associated document manager and we must fail + return VSConstants.E_FAIL; + } + + + public virtual int OpenItemWithSpecific(uint itemId, uint editorFlags, ref Guid editorType, string physicalView, ref Guid logicalView, IntPtr docDataExisting, out IVsWindowFrame frame) + { + // Init output params + frame = null; + + HierarchyNode n = this.NodeFromItemId(itemId); + if (n == null) + { + throw new ArgumentException(SR.GetString(SR.ParameterMustBeAValidItemId, CultureInfo.CurrentUICulture), "itemId"); + } + + // Delegate to the document manager object that knows how to open the item + DocumentManager documentManager = n.GetDocumentManager(); + if (documentManager != null) + { + return documentManager.OpenWithSpecific(editorFlags, ref editorType, physicalView, ref logicalView, docDataExisting, out frame, WindowFrameShowAction.DoNotShow); + } + + // This node does not have an associated document manager and we must fail + return VSConstants.E_FAIL; + } + + + public virtual int RemoveItem(uint reserved, uint itemId, out int result) + { + HierarchyNode n = this.NodeFromItemId(itemId); + if (n == null) + { + throw new ArgumentException(SR.GetString(SR.ParameterMustBeAValidItemId, CultureInfo.CurrentUICulture), "itemId"); + } + n.Remove(true); + result = 1; + return VSConstants.S_OK; + } + + + public virtual int ReopenItem(uint itemId, ref Guid editorType, string physicalView, ref Guid logicalView, IntPtr docDataExisting, out IVsWindowFrame frame) + { + // Init output params + frame = null; + + HierarchyNode n = this.NodeFromItemId(itemId); + if (n == null) + { + throw new ArgumentException(SR.GetString(SR.ParameterMustBeAValidItemId, CultureInfo.CurrentUICulture), "itemId"); + } + + // Delegate to the document manager object that knows how to open the item + DocumentManager documentManager = n.GetDocumentManager(); + if (documentManager != null) + { + return documentManager.OpenWithSpecific(0, ref editorType, physicalView, ref logicalView, docDataExisting, out frame, WindowFrameShowAction.DoNotShow); + } + + // This node does not have an associated document manager and we must fail + return VSConstants.E_FAIL; + } + + + /// + /// Implements IVsProject3::TransferItem + /// This function is called when an open miscellaneous file is being transferred + /// to our project. The sequence is for the shell to call AddItemWithSpecific and + /// then use TransferItem to transfer the open document to our project. + /// + /// Old document name + /// New document name + /// Optional frame if the document is open + /// + public virtual int TransferItem(string oldMkDoc, string newMkDoc, IVsWindowFrame frame) + { + // Fail if hierarchy already closed + if (this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return VSConstants.E_FAIL; + } + //Fail if the document names passed are null. + if (oldMkDoc == null || newMkDoc == null) + return VSConstants.E_INVALIDARG; + + int hr = VSConstants.S_OK; + VSDOCUMENTPRIORITY[] priority = new VSDOCUMENTPRIORITY[1]; + uint itemid = VSConstants.VSITEMID_NIL; + uint cookie = 0; + uint grfFlags = 0; + + IVsRunningDocumentTable pRdt = GetService(typeof(IVsRunningDocumentTable)) as IVsRunningDocumentTable; + if (pRdt == null) + return VSConstants.E_ABORT; + + string doc; + int found; + IVsHierarchy pHier; + uint id, readLocks, editLocks; + IntPtr docdataForCookiePtr = IntPtr.Zero; + IntPtr docDataPtr = IntPtr.Zero; + IntPtr hierPtr = IntPtr.Zero; + + // We get the document from the running doc table so that we can see if it is transient + try + { + ErrorHandler.ThrowOnFailure(pRdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, oldMkDoc, out pHier, out id, out docdataForCookiePtr, out cookie)); + } + finally + { + if (docdataForCookiePtr != IntPtr.Zero) + Marshal.Release(docdataForCookiePtr); + } + + //Get the document info + try + { + ErrorHandler.ThrowOnFailure(pRdt.GetDocumentInfo(cookie, out grfFlags, out readLocks, out editLocks, out doc, out pHier, out id, out docDataPtr)); + } + finally + { + if (docDataPtr != IntPtr.Zero) + Marshal.Release(docDataPtr); + } + + // Now see if the document is in the project. If not, we fail + try + { + ErrorHandler.ThrowOnFailure(IsDocumentInProject(newMkDoc, out found, priority, out itemid)); + Debug.Assert(itemid != VSConstants.VSITEMID_NIL && itemid != VSConstants.VSITEMID_ROOT); + hierPtr = Marshal.GetComInterfaceForObject(this, typeof(IVsUIHierarchy)); + // Now rename the document + ErrorHandler.ThrowOnFailure(pRdt.RenameDocument(oldMkDoc, newMkDoc, hierPtr, itemid)); + } + finally + { + if (hierPtr != IntPtr.Zero) + Marshal.Release(hierPtr); + } + + //Change the caption if we are passed a window frame + if (frame != null) + { + string caption = "%2"; + hr = frame.SetProperty((int)(__VSFPROPID.VSFPROPID_OwnerCaption), caption); + } + return hr; + } + + #endregion + + #region IVsProjectBuidSystem Members + public virtual int SetHostObject(string targetName, string taskName, object hostObject) + { + Debug.Assert(targetName != null && taskName != null && this.buildProject != null && this.buildProject.Targets != null); + + if (targetName == null || taskName == null || this.buildProject == null || this.buildProject.Targets == null) + { + return VSConstants.E_INVALIDARG; + } + + this.buildProject.ProjectCollection.HostServices.RegisterHostObject(this.buildProject.FullPath, targetName, taskName, (Microsoft.Build.Framework.ITaskHost)hostObject); + + return VSConstants.S_OK; + } + + public int BuildTarget(string targetName, out bool success) + { + success = false; + + MSBuildResult result = this.Build(targetName); + + if (result == MSBuildResult.Successful) + { + success = true; + } + + return VSConstants.S_OK; + } + + public virtual int CancelBatchEdit() + { + return VSConstants.E_NOTIMPL; + } + + public virtual int EndBatchEdit() + { + return VSConstants.E_NOTIMPL; + } + + public virtual int StartBatchEdit() + { + return VSConstants.E_NOTIMPL; + } + + /// + /// Used to determine the kind of build system, in VS 2005 there's only one defined kind: MSBuild + /// + /// + /// + public virtual int GetBuildSystemKind(out uint kind) + { + kind = (uint)_BuildSystemKindFlags2.BSK_MSBUILD_VS10; + return VSConstants.S_OK; + } + + #endregion + + #region IVsComponentUser methods + + /// + /// Add Components to the Project. + /// Used by the environment to add components specified by the user in the Component Selector dialog + /// to the specified project + /// + /// The component operation to be performed. + /// Number of components to be added + /// array of component selector data + /// Handle to the component picker dialog + /// Result to be returned to the caller + public virtual int AddComponent(VSADDCOMPOPERATION dwAddCompOperation, uint cComponents, System.IntPtr[] rgpcsdComponents, System.IntPtr hwndDialog, VSADDCOMPRESULT[] pResult) + { + if (rgpcsdComponents == null || pResult == null) + { + return VSConstants.E_FAIL; + } + + //initalize the out parameter + pResult[0] = VSADDCOMPRESULT.ADDCOMPRESULT_Success; + + IReferenceContainer references = GetReferenceContainer(); + if (null == references) + { + // This project does not support references or the reference container was not created. + // In both cases this operation is not supported. + return VSConstants.E_NOTIMPL; + } + for (int cCount = 0; cCount < cComponents; cCount++) + { + VSCOMPONENTSELECTORDATA selectorData = new VSCOMPONENTSELECTORDATA(); + IntPtr ptr = rgpcsdComponents[cCount]; + selectorData = (VSCOMPONENTSELECTORDATA)Marshal.PtrToStructure(ptr, typeof(VSCOMPONENTSELECTORDATA)); + if (null == references.AddReferenceFromSelectorData(selectorData)) + { + //Skip further proccessing since a reference has to be added + pResult[0] = VSADDCOMPRESULT.ADDCOMPRESULT_Failure; + return VSConstants.S_OK; + } + } + return VSConstants.S_OK; + } + #endregion + + #region IVsDependencyProvider Members + public int EnumDependencies(out IVsEnumDependencies enumDependencies) + { + enumDependencies = new EnumDependencies(this.buildDependencyList); + return VSConstants.S_OK; + } + + public int OpenDependency(string szDependencyCanonicalName, out IVsDependency dependency) + { + dependency = null; + return VSConstants.S_OK; + } + + #endregion + + #region IVsSccProject2 Members + /// + /// This method is called to determine which files should be placed under source control for a given VSITEMID within this hierarchy. + /// + /// Identifier for the VSITEMID being queried. + /// Pointer to an array of CALPOLESTR strings containing the file names for this item. + /// Pointer to a CADWORD array of flags stored in DWORDs indicating that some of the files have special behaviors. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int GetSccFiles(uint itemid, CALPOLESTR[] stringsOut, CADWORD[] flagsOut) + { + if (itemid == VSConstants.VSITEMID_SELECTION) + { + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "itemid"); + } + + HierarchyNode n = this.NodeFromItemId(itemid); + if (n == null) + { + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "itemid"); + } + + List files = new List(); + List flags = new List(); + + n.GetSccFiles(files, flags); + + if (stringsOut != null && stringsOut.Length > 0) + { + stringsOut[0] = Utilities.CreateCALPOLESTR(files); + } + + if (flagsOut != null && flagsOut.Length > 0) + { + flagsOut[0] = Utilities.CreateCADWORD(flags); + } + + return VSConstants.S_OK; + } + + /// + /// This method is called to discover special (hidden files) associated with a given VSITEMID within this hierarchy. + /// + /// Identifier for the VSITEMID being queried. + /// One of the files associated with the node + /// Pointer to an array of CALPOLESTR strings containing the file names for this item. + /// Pointer to a CADWORD array of flags stored in DWORDs indicating that some of the files have special behaviors. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + /// This method is called to discover any special or hidden files associated with an item in the project hierarchy. It is called when GetSccFiles returns with the SFF_HasSpecialFiles flag set for any of the files associated with the node. + public virtual int GetSccSpecialFiles(uint itemid, string sccFile, CALPOLESTR[] stringsOut, CADWORD[] flagsOut) + { + if (itemid == VSConstants.VSITEMID_SELECTION) + { + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "itemid"); + } + + HierarchyNode n = this.NodeFromItemId(itemid); + if (n == null) + { + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "itemid"); + } + + List files = new List(); + + List flags = new List(); + + n.GetSccSpecialFiles(sccFile, files, flags); + + if (stringsOut != null && stringsOut.Length > 0) + { + stringsOut[0] = Utilities.CreateCALPOLESTR(files); + } + + if (flagsOut != null && flagsOut.Length > 0) + { + flagsOut[0] = Utilities.CreateCADWORD(flags); + } + + return VSConstants.S_OK; + + } + + /// + /// This method is called by the source control portion of the environment to inform the project of changes to the source control glyph on various nodes. + /// + /// Count of changed nodes. + /// An array of VSITEMID identifiers of the changed nodes. + /// An array of VsStateIcon glyphs representing the new state of the corresponding item in rgitemidAffectedNodes. + /// An array of status flags from SccStatus corresponding to rgitemidAffectedNodes. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int SccGlyphChanged(int affectedNodes, uint[] itemidAffectedNodes, VsStateIcon[] newGlyphs, uint[] newSccStatus) + { + // if all the paramaters are null adn the count is 0, it means scc wants us to updated everything + if (affectedNodes == 0 && itemidAffectedNodes == null && newGlyphs == null && newSccStatus == null) + { + this.ReDraw(UIHierarchyElement.SccState); + this.UpdateSccStateIcons(); + } + else if (affectedNodes > 0 && itemidAffectedNodes != null && newGlyphs != null && newSccStatus != null) + { + for (int i = 0; i < affectedNodes; i++) + { + HierarchyNode n = this.NodeFromItemId(itemidAffectedNodes[i]); + if (n == null) + { + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "itemidAffectedNodes"); + } + + n.ReDraw(UIHierarchyElement.SccState); + } + } + return VSConstants.S_OK; + } + + /// + /// This method is called by the source control portion of the environment when a project is initially added to source control, or to change some of the project's settings. + /// + /// String, opaque to the project, that identifies the project location on the server. Persist this string in the project file. + /// String, opaque to the project, that identifies the path to the server. Persist this string in the project file. + /// String, opaque to the project, that identifies the local path to the project. Persist this string in the project file. + /// String, opaque to the project, that identifies the source control package. Persist this string in the project file. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int SetSccLocation(string sccProjectName, string sccAuxPath, string sccLocalPath, string sccProvider) + { + if (this.IsSccDisabled) + { + throw new NotImplementedException(); + } + + if (sccProjectName == null) + { + throw new ArgumentNullException("sccProjectName"); + } + + if (sccAuxPath == null) + { + throw new ArgumentNullException("sccAuxPath"); + } + + if (sccLocalPath == null) + { + throw new ArgumentNullException("sccLocalPath"); + } + + if (sccProvider == null) + { + throw new ArgumentNullException("sccProvider"); + } + + // Save our settings (returns true if something changed) + if (!this.SetSccSettings(sccProjectName, sccLocalPath, sccAuxPath, sccProvider)) + { + return VSConstants.S_OK; + } + + bool unbinding = (sccProjectName.Length == 0 && sccProvider.Length == 0); + + if (unbinding || this.QueryEditProjectFile(false)) + { + this.buildProject.SetProperty(ProjectFileConstants.SccProjectName, sccProjectName); + this.buildProject.SetProperty(ProjectFileConstants.SccProvider, sccProvider); + this.buildProject.SetProperty(ProjectFileConstants.SccAuxPath, sccAuxPath); + this.buildProject.SetProperty(ProjectFileConstants.SccLocalPath, sccLocalPath); + } + + this.isRegisteredWithScc = true; + + return VSConstants.S_OK; + } + #endregion + + #region IVsProjectSpecialFiles Members + /// + /// Allows you to query the project for special files and optionally create them. + /// + /// __PSFFILEID of the file + /// __PSFFLAGS flags for the file + /// The itemid of the node in the hierarchy + /// The file name of the special file. + /// + public virtual int GetFile(int fileId, uint flags, out uint itemid, out string fileName) + { + itemid = VSConstants.VSITEMID_NIL; + fileName = String.Empty; + + // We need to return S_OK, otherwise the property page tabs will not be shown. + return VSConstants.E_NOTIMPL; + } + #endregion + + #region IAggregatedHierarchy Members + + /// + /// Get the inner object of an aggregated hierarchy + /// + /// A HierarchyNode + public virtual HierarchyNode GetInner() + { + return this; + } + + #endregion + + #region IBuildDependencyUpdate Members + + public virtual IVsBuildDependency[] BuildDependencies + { + get + { + return this.buildDependencyList.ToArray(); + } + } + + public virtual void AddBuildDependency(IVsBuildDependency dependency) + { + if (this.isClosed || dependency == null) + { + return; + } + + if (!this.buildDependencyList.Contains(dependency)) + { + this.buildDependencyList.Add(dependency); + } + } + + public virtual void RemoveBuildDependency(IVsBuildDependency dependency) + { + if (this.isClosed || dependency == null) + { + return; + } + + if (this.buildDependencyList.Contains(dependency)) + { + this.buildDependencyList.Remove(dependency); + } + } + + #endregion + + #region IReferenceDataProvider Members + /// + /// Returns the reference container node. + /// + /// + public IReferenceContainer GetReferenceContainer() + { + return this.FindChild(ReferenceContainerNode.ReferencesNodeVirtualName) as IReferenceContainer; + } + + #endregion + + #region IProjectEventsListener Members + public bool IsProjectEventsListener + { + get { return this.isProjectEventsListener; } + set { this.isProjectEventsListener = value; } + } + #endregion + + #region IProjectEventsProvider Members + + /// + /// Defines the provider for the project events + /// + IProjectEvents IProjectEventsProvider.ProjectEventsProvider + { + get + { + return this.projectEventsProvider; + } + set + { + if (null != this.projectEventsProvider) + { + this.projectEventsProvider.AfterProjectFileOpened -= this.OnAfterProjectOpen; + } + this.projectEventsProvider = value; + if (null != this.projectEventsProvider) + { + this.projectEventsProvider.AfterProjectFileOpened += this.OnAfterProjectOpen; + } + } + } + + #endregion + + #region IVsAggregatableProject Members + + /// + /// Retrieve the list of project GUIDs that are aggregated together to make this project. + /// + /// Semi colon separated list of Guids. Typically, the last GUID would be the GUID of the base project factory + /// HResult + public int GetAggregateProjectTypeGuids(out string projectTypeGuids) + { + projectTypeGuids = this.GetProjectProperty(ProjectFileConstants.ProjectTypeGuids); + // In case someone manually removed this from our project file, default to our project without flavors + if (String.IsNullOrEmpty(projectTypeGuids)) + projectTypeGuids = this.ProjectGuid.ToString("B"); + return VSConstants.S_OK; + } + + /// + /// This is where the initialization occurs. + /// + public virtual int InitializeForOuter(string filename, string location, string name, uint flags, ref Guid iid, out IntPtr projectPointer, out int canceled) + { + canceled = 0; + projectPointer = IntPtr.Zero; + + // Initialize the project + this.Load(filename, location, name, flags, ref iid, out canceled); + + if (canceled != 1) + { + // Set ourself as the project + return Marshal.QueryInterface(Marshal.GetIUnknownForObject(this), ref iid, out projectPointer); + } + + return VSConstants.OLE_E_PROMPTSAVECANCELLED; + } + + /// + /// This is called after the project is done initializing the different layer of the aggregations + /// + /// HResult + public virtual int OnAggregationComplete() + { + return VSConstants.S_OK; + } + + /// + /// Set the list of GUIDs that are aggregated together to create this project. + /// + /// Semi-colon separated list of GUIDs, the last one is usually the project factory of the base project factory + /// HResult + public int SetAggregateProjectTypeGuids(string projectTypeGuids) + { + this.SetProjectProperty(ProjectFileConstants.ProjectTypeGuids, projectTypeGuids); + return VSConstants.S_OK; + } + + /// + /// We are always the inner most part of the aggregation + /// and as such we don't support setting an inner project + /// + public int SetInnerProject(object innerProject) + { + return VSConstants.E_NOTIMPL; + } + + #endregion + + #region IVsProjectFlavorCfgProvider Members + + int IVsProjectFlavorCfgProvider.CreateProjectFlavorCfg(IVsCfg pBaseProjectCfg, out IVsProjectFlavorCfg ppFlavorCfg) + { + // Our config object is also our IVsProjectFlavorCfg object + ppFlavorCfg = pBaseProjectCfg as IVsProjectFlavorCfg; + + return VSConstants.S_OK; + } + + #endregion + + #region IVsBuildPropertyStorage Members + + /// + /// Get the property of an item + /// + /// ItemID + /// Name of the property + /// Value of the property (out parameter) + /// HRESULT + int IVsBuildPropertyStorage.GetItemAttribute(uint item, string attributeName, out string attributeValue) + { + attributeValue = null; + + HierarchyNode node = NodeFromItemId(item); + if (node == null) + throw new ArgumentException("Invalid item id", "item"); + + attributeValue = node.ItemNode.GetMetadata(attributeName); + return VSConstants.S_OK; + } + + /// + /// Get the value of the property in the project file + /// + /// Name of the property to remove + /// Configuration for which to remove the property + /// Project or user file (_PersistStorageType) + /// Value of the property (out parameter) + /// HRESULT + int IVsBuildPropertyStorage.GetPropertyValue(string propertyName, string configName, uint storage, out string propertyValue) + { + // TODO: when adding support for User files, we need to update this method + propertyValue = null; + if (string.IsNullOrEmpty(configName)) + { + propertyValue = this.GetProjectProperty(propertyName); + } + else + { + IVsCfg configurationInterface; + ErrorHandler.ThrowOnFailure(this.ConfigProvider.GetCfgOfName(configName, string.Empty, out configurationInterface)); + ProjectConfig config = (ProjectConfig)configurationInterface; + propertyValue = config.GetConfigurationProperty(propertyName, true); + } + return VSConstants.S_OK; + } + + /// + /// Delete a property + /// In our case this simply mean defining it as null + /// + /// Name of the property to remove + /// Configuration for which to remove the property + /// Project or user file (_PersistStorageType) + /// HRESULT + int IVsBuildPropertyStorage.RemoveProperty(string propertyName, string configName, uint storage) + { + return ((IVsBuildPropertyStorage)this).SetPropertyValue(propertyName, configName, storage, null); + } + + /// + /// Set a property on an item + /// + /// ItemID + /// Name of the property + /// New value for the property + /// HRESULT + int IVsBuildPropertyStorage.SetItemAttribute(uint item, string attributeName, string attributeValue) + { + HierarchyNode node = NodeFromItemId(item); + + if (node == null) + throw new ArgumentException("Invalid item id", "item"); + + node.ItemNode.SetMetadata(attributeName, attributeValue); + return VSConstants.S_OK; + } + + /// + /// Set a project property + /// + /// Name of the property to set + /// Configuration for which to set the property + /// Project file or user file (_PersistStorageType) + /// New value for that property + /// HRESULT + int IVsBuildPropertyStorage.SetPropertyValue(string propertyName, string configName, uint storage, string propertyValue) + { + // TODO: when adding support for User files, we need to update this method + if (string.IsNullOrEmpty(configName)) + { + this.SetProjectProperty(propertyName, propertyValue); + } + else + { + IVsCfg configurationInterface; + ErrorHandler.ThrowOnFailure(this.ConfigProvider.GetCfgOfName(configName, string.Empty, out configurationInterface)); + ProjectConfig config = (ProjectConfig)configurationInterface; + config.SetConfigurationProperty(propertyName, propertyValue); + } + return VSConstants.S_OK; + } + + #endregion + + #region private helper methods + + /// + /// Initialize projectNode + /// + private void Initialize() + { + this.ID = VSConstants.VSITEMID_ROOT; + this.tracker = new TrackDocumentsHelper(this); + } + + /// + /// Add an item to the hierarchy based on the item path + /// + /// Item to add + /// Added node + private HierarchyNode AddIndependentFileNode(MSBuild.ProjectItem item) + { + HierarchyNode currentParent = GetItemParentNode(item); + return AddFileNodeToNode(item, currentParent); + } + + /// + /// Add a dependent file node to the hierarchy + /// + /// msbuild item to add + /// Parent Node + /// Added node + private HierarchyNode AddDependentFileNodeToNode(MSBuild.ProjectItem item, HierarchyNode parentNode) + { + FileNode node = this.CreateDependentFileNode(new ProjectElement(this, item, false)); + parentNode.AddChild(node); + + // Make sure to set the HasNameRelation flag on the dependent node if it is related to the parent by name + if (!node.HasParentNodeNameRelation && string.Compare(node.GetRelationalName(), parentNode.GetRelationalName(), StringComparison.OrdinalIgnoreCase) == 0) + { + node.HasParentNodeNameRelation = true; + } + + return node; + } + + /// + /// Add a file node to the hierarchy + /// + /// msbuild item to add + /// Parent Node + /// Added node + private HierarchyNode AddFileNodeToNode(MSBuild.ProjectItem item, HierarchyNode parentNode) + { + FileNode node = this.CreateFileNode(new ProjectElement(this, item, false)); + parentNode.AddChild(node); + return node; + } + + /// + /// Get the parent node of an msbuild item + /// + /// msbuild item + /// parent node + private HierarchyNode GetItemParentNode(MSBuild.ProjectItem item) + { + HierarchyNode currentParent = this; + string strPath = item.EvaluatedInclude; + + strPath = Path.GetDirectoryName(strPath); + if (strPath.Length > 0) + { + // Use the relative to verify the folders... + currentParent = this.CreateFolderNodes(strPath); + } + return currentParent; + } + + private MSBuildExecution.ProjectPropertyInstance GetMsBuildProperty(string propertyName, bool resetCache) + { + if (resetCache || this.currentConfig == null) + { + // Get properties from project file and cache it + this.SetCurrentConfiguration(); + this.currentConfig = this.buildProject.CreateProjectInstance(); + } + + if (this.currentConfig == null) + throw new Exception(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.FailedToRetrieveProperties, CultureInfo.CurrentUICulture), propertyName)); + + // return property asked for + return this.currentConfig.GetProperty(propertyName); + } + + private string GetOutputPath(MSBuildExecution.ProjectInstance properties) + { + this.currentConfig = properties; + string outputPath = GetProjectProperty("OutputPath"); + + if (!String.IsNullOrEmpty(outputPath)) + { + outputPath = outputPath.Replace('/', Path.DirectorySeparatorChar); + if (outputPath[outputPath.Length - 1] != Path.DirectorySeparatorChar) + outputPath += Path.DirectorySeparatorChar; + } + + return outputPath; + } + + private bool GetBoolAttr(MSBuildExecution.ProjectInstance properties, string name) + { + this.currentConfig = properties; + string s = GetProjectProperty(name); + + return (s != null && s.ToUpperInvariant().Trim() == "TRUE"); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Globalization", "CA1308:NormalizeStringsToUppercase")] + private string GetAssemblyName(MSBuildExecution.ProjectInstance properties) + { + this.currentConfig = properties; + string name = null; + + name = GetProjectProperty(ProjectFileConstants.AssemblyName); + if (name == null) + name = this.Caption; + + string outputtype = GetProjectProperty(ProjectFileConstants.OutputType, false); + + if (outputtype == "library") + { + outputtype = outputtype.ToLowerInvariant(); + name += ".dll"; + } + else + { + name += ".exe"; + } + + return name; + } + + /// + /// Updates our scc project settings. + /// + /// String, opaque to the project, that identifies the project location on the server. Persist this string in the project file. + /// String, opaque to the project, that identifies the path to the server. Persist this string in the project file. + /// String, opaque to the project, that identifies the local path to the project. Persist this string in the project file. + /// String, opaque to the project, that identifies the source control package. Persist this string in the project file. + /// Returns true if something changed. + private bool SetSccSettings(string sccProjectName, string sccLocalPath, string sccAuxPath, string sccProvider) + { + bool changed = false; + Debug.Assert(sccProjectName != null && sccLocalPath != null && sccAuxPath != null && sccProvider != null); + if (String.Compare(sccProjectName, this.sccProjectName, StringComparison.OrdinalIgnoreCase) != 0 || + String.Compare(sccLocalPath, this.sccLocalPath, StringComparison.OrdinalIgnoreCase) != 0 || + String.Compare(sccAuxPath, this.sccAuxPath, StringComparison.OrdinalIgnoreCase) != 0 || + String.Compare(sccProvider, this.sccProvider, StringComparison.OrdinalIgnoreCase) != 0) + { + changed = true; + this.sccProjectName = sccProjectName; + this.sccLocalPath = sccLocalPath; + this.sccAuxPath = sccAuxPath; + this.sccProvider = sccProvider; + } + + + return changed; + } + + /// + /// Sets the scc info from the project file. + /// + private void InitSccInfo() + { + this.sccProjectName = this.GetProjectProperty(ProjectFileConstants.SccProjectName); + this.sccLocalPath = this.GetProjectProperty(ProjectFileConstants.SccLocalPath); + this.sccProvider = this.GetProjectProperty(ProjectFileConstants.SccProvider); + this.sccAuxPath = this.GetProjectProperty(ProjectFileConstants.SccAuxPath); + } + + private void OnAfterProjectOpen(object sender, AfterProjectFileOpenedEventArgs e) + { + this.projectOpened = true; + } + + private static XmlElement WrapXmlFragment(XmlDocument document, XmlElement root, Guid flavor, string configuration, string fragment) + { + XmlElement node = document.CreateElement(ProjectFileConstants.FlavorProperties); + XmlAttribute attribute = document.CreateAttribute(ProjectFileConstants.Guid); + attribute.Value = flavor.ToString("B"); + node.Attributes.Append(attribute); + if (!String.IsNullOrEmpty(configuration)) + { + attribute = document.CreateAttribute(ProjectFileConstants.Configuration); + attribute.Value = configuration; + node.Attributes.Append(attribute); + } + node.InnerXml = fragment; + root.AppendChild(node); + return node; + } + + /// + /// Sets the project guid from the project file. If no guid is found a new one is created and assigne for the instance project guid. + /// + private void SetProjectGuidFromProjectFile() + { + string projectGuid = this.GetProjectProperty(ProjectFileConstants.ProjectGuid); + if (String.IsNullOrEmpty(projectGuid)) + { + this.projectIdGuid = Guid.NewGuid(); + } + else + { + Guid guid = new Guid(projectGuid); + if (guid != this.projectIdGuid) + { + this.projectIdGuid = guid; + } + } + } + + /// + /// Helper for sharing common code between Build() and BuildAsync() + /// + /// + /// + private bool BuildPrelude(IVsOutputWindowPane output) + { + bool engineLogOnlyCritical = false; + // If there is some output, then we can ask the build engine to log more than + // just the critical events. + if (null != output) + { + engineLogOnlyCritical = BuildEngine.OnlyLogCriticalEvents; + BuildEngine.OnlyLogCriticalEvents = false; + } + + this.SetOutputLogger(output); + return engineLogOnlyCritical; + } + + /// + /// Recusively parses the tree and closes all nodes. + /// + /// The subtree to close. + private static void CloseAllNodes(HierarchyNode node) + { + for (HierarchyNode n = node.FirstChild; n != null; n = n.NextSibling) + { + if (n.FirstChild != null) + { + CloseAllNodes(n); + } + + n.Close(); + } + } + + /// + /// Set the build project with the new project instance value + /// + /// The new build project instance + private void SetBuildProject(MSBuild.Project project) + { + this.buildProject = project; + if (this.buildProject != null) + { + SetupProjectGlobalPropertiesThatAllProjectSystemsMustSet(); + } + } + + /// + /// Setup the global properties for project instance. + /// + private void SetupProjectGlobalPropertiesThatAllProjectSystemsMustSet() + { + string solutionDirectory = null; + string solutionFile = null; + string userOptionsFile = null; + + IVsSolution solution = this.Site.GetService(typeof(SVsSolution)) as IVsSolution; + if (solution != null) + { + // We do not want to throw. If we cannot set the solution related constants we set them to empty string. + solution.GetSolutionInfo(out solutionDirectory, out solutionFile, out userOptionsFile); + } + + if (solutionDirectory == null) + { + solutionDirectory = String.Empty; + } + + if (solutionFile == null) + { + solutionFile = String.Empty; + } + + string solutionFileName = (solutionFile.Length == 0) ? String.Empty : Path.GetFileName(solutionFile); + + string solutionName = (solutionFile.Length == 0) ? String.Empty : Path.GetFileNameWithoutExtension(solutionFile); + + string solutionExtension = String.Empty; + if (solutionFile.Length > 0 && Path.HasExtension(solutionFile)) + { + solutionExtension = Path.GetExtension(solutionFile); + } + + this.buildProject.SetGlobalProperty(GlobalProperty.SolutionDir.ToString(), solutionDirectory); + this.buildProject.SetGlobalProperty(GlobalProperty.SolutionPath.ToString(), solutionFile); + this.buildProject.SetGlobalProperty(GlobalProperty.SolutionFileName.ToString(), solutionFileName); + this.buildProject.SetGlobalProperty(GlobalProperty.SolutionName.ToString(), solutionName); + this.buildProject.SetGlobalProperty(GlobalProperty.SolutionExt.ToString(), solutionExtension); + + // Other misc properties + this.buildProject.SetGlobalProperty(GlobalProperty.BuildingInsideVisualStudio.ToString(), "true"); + this.buildProject.SetGlobalProperty(GlobalProperty.Configuration.ToString(), ProjectConfig.Debug); + this.buildProject.SetGlobalProperty(GlobalProperty.Platform.ToString(), ProjectConfig.AnyCPU); + + // DevEnvDir property + object installDirAsObject = null; + + IVsShell shell = this.Site.GetService(typeof(SVsShell)) as IVsShell; + if (shell != null) + { + // We do not want to throw. If we cannot set the solution related constants we set them to empty string. + shell.GetProperty((int)__VSSPROPID.VSSPROPID_InstallDirectory, out installDirAsObject); + } + + string installDir = ((string)installDirAsObject); + + if (String.IsNullOrEmpty(installDir)) + { + installDir = String.Empty; + } + else + { + // Ensure that we have traimnling backslash as this is done for the langproj macros too. + if (installDir[installDir.Length - 1] != Path.DirectorySeparatorChar) + { + installDir += Path.DirectorySeparatorChar; + } + } + + this.buildProject.SetGlobalProperty(GlobalProperty.DevEnvDir.ToString(), installDir); + } + + /// + /// Attempts to lock in the privilege of running a build in Visual Studio. + /// + /// false if this build was called for by the Solution Build Manager; true otherwise. + /// + /// Need to claim the UI thread for build under the following conditions: + /// 1. The build must use a resource that uses the UI thread, such as + /// - you set HostServices and you have a host object which requires (even indirectly) the UI thread (VB and C# compilers do this for instance.) + /// or, + /// 2. The build requires the in-proc node AND waits on the UI thread for the build to complete, such as: + /// - you use a ProjectInstance to build, or + /// - you have specified a host object, whether or not it requires the UI thread, or + /// - you set HostServices and you have specified a node affinity. + /// - In addition to the above you also call submission.Execute(), or you call submission.ExecuteAsync() and then also submission.WaitHandle.Wait*(). + /// + /// A value indicating whether a build may proceed. + /// + /// This method must be called on the UI thread. + /// + private bool TryBeginBuild(bool designTime, bool requiresUIThread = false) + { + IVsBuildManagerAccessor accessor = null; + + if (this.Site != null) + { + accessor = this.Site.GetService(typeof(SVsBuildManagerAccessor)) as IVsBuildManagerAccessor; + } + + bool releaseUIThread = false; + + try + { + // If the SVsBuildManagerAccessor service is absent, we're not running within Visual Studio. + if (accessor != null) + { + if (requiresUIThread) + { + int result = accessor.ClaimUIThreadForBuild(); + if (result < 0) + { + // Not allowed to claim the UI thread right now. Try again later. + return false; + } + + releaseUIThread = true; // assume we need to release this immediately until we get through the whole gauntlet. + } + + if (designTime) + { + int result = accessor.BeginDesignTimeBuild(); + if (result < 0) + { + // Not allowed to begin a design-time build at this time. Try again later. + return false; + } + } + + // We obtained all the resources we need. So don't release the UI thread until after the build is finished. + releaseUIThread = false; + } + else + { + BuildParameters buildParameters = new BuildParameters(this.buildEngine); + BuildManager.DefaultBuildManager.BeginBuild(buildParameters); + } + + this.buildInProcess = true; + return true; + } + finally + { + // If we were denied the privilege of starting a design-time build, + // we need to release the UI thread. + if (releaseUIThread) + { + Debug.Assert(accessor != null, "We think we need to release the UI thread for an accessor we don't have!"); + accessor.ReleaseUIThreadForBuild(); + } + } + } + + /// + /// Lets Visual Studio know that we're done with our design-time build so others can use the build manager. + /// + /// The build submission that built, if any. + /// This must be the same value as the one passed to . + /// This must be the same value as the one passed to . + /// + /// This method must be called on the UI thread. + /// + private void EndBuild(BuildSubmission submission, bool designTime, bool requiresUIThread = false) + { + IVsBuildManagerAccessor accessor = null; + + if (this.Site != null) + { + accessor = this.Site.GetService(typeof(SVsBuildManagerAccessor)) as IVsBuildManagerAccessor; + } + + if (accessor != null) + { + // It's very important that we try executing all three end-build steps, even if errors occur partway through. + try + { + if (submission != null) + { + Marshal.ThrowExceptionForHR(accessor.UnregisterLoggers(submission.SubmissionId)); + } + } + catch (Exception ex) + { + if (ErrorHandler.IsCriticalException(ex)) + { + throw; + } + + Trace.TraceError(ex.ToString()); + } + + try + { + if (designTime) + { + Marshal.ThrowExceptionForHR(accessor.EndDesignTimeBuild()); + } + } + catch (Exception ex) + { + if (ErrorHandler.IsCriticalException(ex)) + { + throw; + } + + Trace.TraceError(ex.ToString()); + } + + + try + { + if (requiresUIThread) + { + Marshal.ThrowExceptionForHR(accessor.ReleaseUIThreadForBuild()); + } + } + catch (Exception ex) + { + if (ErrorHandler.IsCriticalException(ex)) + { + throw; + } + + Trace.TraceError(ex.ToString()); + } + } + else + { + BuildManager.DefaultBuildManager.EndBuild(); + } + + this.buildInProcess = false; + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectOptions.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectOptions.cs new file mode 100644 index 0000000000..1b7e91b470 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectOptions.cs @@ -0,0 +1,97 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Specialized; +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.VisualStudio.Project +{ + public class ProjectOptions : System.CodeDom.Compiler.CompilerParameters + { + public ModuleKindFlags ModuleKind { get; set; } + + public bool EmitManifest { get; set; } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public StringCollection DefinedPreprocessorSymbols { get; set; } + + public string XmlDocFileName { get; set; } + + public string RecursiveWildcard { get; set; } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public StringCollection ReferencedModules { get; set; } + + public string Win32Icon { get; set; } + + public bool PdbOnly { get; set; } + + public bool Optimize { get; set; } + + public bool IncrementalCompile { get; set; } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + public int[] SuppressedWarnings { get; set; } + + public bool CheckedArithmetic { get; set; } + + public bool AllowUnsafeCode { get; set; } + + public bool DisplayCommandLineHelp { get; set; } + + public bool SuppressLogo { get; set; } + + public long BaseAddress { get; set; } + + public string BugReportFileName { get; set; } + + /// must be an int if not null + public object CodePage { get; set; } + + public bool EncodeOutputInUtf8 { get; set; } + + public bool FullyQualifyPaths { get; set; } + + public int FileAlignment { get; set; } + + public bool NoStandardLibrary { get; set; } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2227:CollectionPropertiesShouldBeReadOnly")] + public StringCollection AdditionalSearchPaths { get; set; } + + public bool HeuristicReferenceResolution { get; set; } + + public string RootNamespace { get; set; } + + public bool CompileAndExecute { get; set; } + + /// must be an int if not null. + public object UserLocaleId { get; set; } + + public PlatformType TargetPlatform { get; set; } + + public string TargetPlatformLocation { get; set; } + + public ProjectOptions() + { + EmitManifest = true; + ModuleKind = ModuleKindFlags.ConsoleApplication; + } + + public virtual string GetOptionHelp() + { + return null; + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectPackage.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectPackage.cs new file mode 100644 index 0000000000..2bad4f7b0f --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectPackage.cs @@ -0,0 +1,93 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Defines abstract package. + /// + [ComVisible(true)] + [CLSCompliant(false)] + public abstract class ProjectPackage : Microsoft.VisualStudio.Shell.Package + { + #region fields + /// + /// This is the place to register all the solution listeners. + /// + private List solutionListeners = new List(); + #endregion + + #region properties + /// + /// Add your listener to this list. They should be added in the overridden Initialize befaore calling the base. + /// + protected internal IList SolutionListeners + { + get + { + return this.solutionListeners; + } + } + #endregion + + #region methods + protected override void Initialize() + { + base.Initialize(); + + // Subscribe to the solution events + this.solutionListeners.Add(new SolutionListenerForProjectReferenceUpdate(this)); + this.solutionListeners.Add(new SolutionListenerForProjectOpen(this)); + this.solutionListeners.Add(new SolutionListenerForBuildDependencyUpdate(this)); + this.solutionListeners.Add(new SolutionListenerForProjectEvents(this)); + + foreach(SolutionListener solutionListener in this.solutionListeners) + { + solutionListener.Init(); + } + } + + protected override void Dispose(bool disposing) + { + // Unadvise solution listeners. + try + { + if(disposing) + { + foreach(SolutionListener solutionListener in this.solutionListeners) + { + solutionListener.Dispose(); + } + + // Dispose the UIThread singleton. + UIThread.Instance.Dispose(); + } + } + finally + { + + base.Dispose(disposing); + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectReferenceNode.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectReferenceNode.cs new file mode 100644 index 0000000000..fe4cca1805 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectReferenceNode.cs @@ -0,0 +1,541 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.VisualStudio.Project +{ + [CLSCompliant(false), ComVisible(true)] + public class ProjectReferenceNode : ReferenceNode + { + #region fieds + /// + /// The name of the assembly this refernce represents + /// + private Guid referencedProjectGuid; + + private string referencedProjectName = String.Empty; + + private string referencedProjectRelativePath = String.Empty; + + private string referencedProjectFullPath = String.Empty; + + private BuildDependency buildDependency; + + /// + /// This is a reference to the automation object for the referenced project. + /// + private EnvDTE.Project referencedProject; + + /// + /// This state is controlled by the solution events. + /// The state is set to false by OnBeforeUnloadProject. + /// The state is set to true by OnBeforeCloseProject event. + /// + private bool canRemoveReference = true; + + /// + /// Possibility for solution listener to update the state on the dangling reference. + /// It will be set in OnBeforeUnloadProject then the nopde is invalidated then it is reset to false. + /// + private bool isNodeValid; + + #endregion + + #region properties + + public override string Url + { + get + { + return this.referencedProjectFullPath; + } + } + + public override string Caption + { + get + { + return this.referencedProjectName; + } + } + + internal Guid ReferencedProjectGuid + { + get + { + return this.referencedProjectGuid; + } + } + + /// + /// Possiblity to shortcut and set the dangling project reference icon. + /// It is ussually manipulated by solution listsneres who handle reference updates. + /// + internal protected bool IsNodeValid + { + get + { + return this.isNodeValid; + } + set + { + this.isNodeValid = value; + } + } + + /// + /// Controls the state whether this reference can be removed or not. Think of the project unload scenario where the project reference should not be deleted. + /// + internal bool CanRemoveReference + { + get + { + return this.canRemoveReference; + } + set + { + this.canRemoveReference = value; + } + } + + internal string ReferencedProjectName + { + get { return this.referencedProjectName; } + } + + /// + /// Gets the automation object for the referenced project. + /// + internal EnvDTE.Project ReferencedProjectObject + { + get + { + // If the referenced project is null then re-read. + if (this.referencedProject == null) + { + + // Search for the project in the collection of the projects in the + // current solution. + EnvDTE.DTE dte = (EnvDTE.DTE)this.ProjectMgr.GetService(typeof(EnvDTE.DTE)); + if ((null == dte) || (null == dte.Solution)) + { + return null; + } + foreach (EnvDTE.Project prj in dte.Solution.Projects) + { + //Skip this project if it is an umodeled project (unloaded) + if (string.Compare(EnvDTE.Constants.vsProjectKindUnmodeled, prj.Kind, StringComparison.OrdinalIgnoreCase) == 0) + { + continue; + } + + // Get the full path of the current project. + EnvDTE.Property pathProperty = null; + try + { + if (prj.Properties == null) + { + continue; + } + + pathProperty = prj.Properties.Item("FullPath"); + if (null == pathProperty) + { + // The full path should alway be availabe, but if this is not the + // case then we have to skip it. + continue; + } + } + catch (ArgumentException) + { + continue; + } + string prjPath = pathProperty.Value.ToString(); + EnvDTE.Property fileNameProperty = null; + // Get the name of the project file. + try + { + fileNameProperty = prj.Properties.Item("FileName"); + if (null == fileNameProperty) + { + // Again, this should never be the case, but we handle it anyway. + continue; + } + } + catch (ArgumentException) + { + continue; + } + prjPath = System.IO.Path.Combine(prjPath, fileNameProperty.Value.ToString()); + + // If the full path of this project is the same as the one of this + // reference, then we have found the right project. + if (NativeMethods.IsSamePath(prjPath, referencedProjectFullPath)) + { + this.referencedProject = prj; + break; + } + } + } + + return this.referencedProject; + } + set + { + this.referencedProject = value; + } + } + + /// + /// Gets the full path to the assembly generated by this project. + /// + internal string ReferencedProjectOutputPath + { + get + { + // Make sure that the referenced project implements the automation object. + if(null == this.ReferencedProjectObject) + { + return null; + } + + // Get the configuration manager from the project. + EnvDTE.ConfigurationManager confManager = this.ReferencedProjectObject.ConfigurationManager; + if(null == confManager) + { + return null; + } + + // Get the active configuration. + EnvDTE.Configuration config = confManager.ActiveConfiguration; + if(null == config) + { + return null; + } + + // Get the output path for the current configuration. + EnvDTE.Property outputPathProperty = config.Properties.Item("OutputPath"); + if (null == outputPathProperty || outputPathProperty.Value == null) + { + return null; + } + + string outputPath = outputPathProperty.Value.ToString(); + + // Ususally the output path is relative to the project path, but it is possible + // to set it as an absolute path. If it is not absolute, then evaluate its value + // based on the project directory. + if(!System.IO.Path.IsPathRooted(outputPath)) + { + string projectDir = System.IO.Path.GetDirectoryName(referencedProjectFullPath); + outputPath = System.IO.Path.Combine(projectDir, outputPath); + } + + // Now get the name of the assembly from the project. + // Some project system throw if the property does not exist. We expect an ArgumentException. + EnvDTE.Property assemblyNameProperty = null; + try + { + assemblyNameProperty = this.ReferencedProjectObject.Properties.Item("OutputFileName"); + } + catch(ArgumentException) + { + } + + if(null == assemblyNameProperty) + { + return null; + } + // build the full path adding the name of the assembly to the output path. + outputPath = System.IO.Path.Combine(outputPath, assemblyNameProperty.Value.ToString()); + + return outputPath; + } + } + + private Automation.OAProjectReference projectReference; + internal override object Object + { + get + { + if(null == projectReference) + { + projectReference = new Automation.OAProjectReference(this); + } + return projectReference; + } + } + #endregion + + #region ctors + /// + /// Constructor for the ReferenceNode. It is called when the project is reloaded, when the project element representing the refernce exists. + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA2234:PassSystemUriObjectsInsteadOfStrings")] + public ProjectReferenceNode(ProjectNode root, ProjectElement element) + : base(root, element) + { + this.referencedProjectRelativePath = this.ItemNode.GetMetadata(ProjectFileConstants.Include); + Debug.Assert(!String.IsNullOrEmpty(this.referencedProjectRelativePath), "Could not retrive referenced project path form project file"); + + string guidString = this.ItemNode.GetMetadata(ProjectFileConstants.Project); + + // Continue even if project setttings cannot be read. + try + { + this.referencedProjectGuid = new Guid(guidString); + + this.buildDependency = new BuildDependency(this.ProjectMgr, this.referencedProjectGuid); + this.ProjectMgr.AddBuildDependency(this.buildDependency); + } + finally + { + Debug.Assert(this.referencedProjectGuid != Guid.Empty, "Could not retrive referenced project guidproject file"); + + this.referencedProjectName = this.ItemNode.GetMetadata(ProjectFileConstants.Name); + + Debug.Assert(!String.IsNullOrEmpty(this.referencedProjectName), "Could not retrive referenced project name form project file"); + } + + Uri uri = new Uri(this.ProjectMgr.BaseURI.Uri, this.referencedProjectRelativePath); + + if(uri != null) + { + this.referencedProjectFullPath = Microsoft.VisualStudio.Shell.Url.Unescape(uri.LocalPath, true); + } + } + + /// + /// constructor for the ProjectReferenceNode + /// + public ProjectReferenceNode(ProjectNode root, string referencedProjectName, string projectPath, string projectReference) + : base(root) + { + Debug.Assert(root != null && !String.IsNullOrEmpty(referencedProjectName) && !String.IsNullOrEmpty(projectReference) + && !String.IsNullOrEmpty(projectPath), "Can not add a reference because the input for adding one is invalid."); + + if (projectReference == null) + { + throw new ArgumentNullException("projectReference"); + } + + this.referencedProjectName = referencedProjectName; + + int indexOfSeparator = projectReference.IndexOf('|'); + + + string fileName = String.Empty; + + // Unfortunately we cannot use the path part of the projectReference string since it is not resolving correctly relative pathes. + if(indexOfSeparator != -1) + { + string projectGuid = projectReference.Substring(0, indexOfSeparator); + this.referencedProjectGuid = new Guid(projectGuid); + if(indexOfSeparator + 1 < projectReference.Length) + { + string remaining = projectReference.Substring(indexOfSeparator + 1); + indexOfSeparator = remaining.IndexOf('|'); + + if(indexOfSeparator == -1) + { + fileName = remaining; + } + else + { + fileName = remaining.Substring(0, indexOfSeparator); + } + } + } + + Debug.Assert(!String.IsNullOrEmpty(fileName), "Can not add a project reference because the input for adding one is invalid."); + + // Did we get just a file or a relative path? + Uri uri = new Uri(projectPath); + + string referenceDir = PackageUtilities.GetPathDistance(this.ProjectMgr.BaseURI.Uri, uri); + + Debug.Assert(!String.IsNullOrEmpty(referenceDir), "Can not add a project reference because the input for adding one is invalid."); + + string justTheFileName = Path.GetFileName(fileName); + this.referencedProjectRelativePath = Path.Combine(referenceDir, justTheFileName); + + this.referencedProjectFullPath = Path.Combine(projectPath, justTheFileName); + + this.buildDependency = new BuildDependency(this.ProjectMgr, this.referencedProjectGuid); + + } + #endregion + + #region methods + protected override NodeProperties CreatePropertiesObject() + { + return new ProjectReferencesProperties(this); + } + + /// + /// The node is added to the hierarchy and then updates the build dependency list. + /// + public override void AddReference() + { + if(this.ProjectMgr == null) + { + return; + } + base.AddReference(); + this.ProjectMgr.AddBuildDependency(this.buildDependency); + return; + } + + /// + /// Overridden method. The method updates the build dependency list before removing the node from the hierarchy. + /// + public override void Remove(bool removeFromStorage) + { + if(this.ProjectMgr == null || !this.CanRemoveReference) + { + return; + } + this.ProjectMgr.RemoveBuildDependency(this.buildDependency); + base.Remove(removeFromStorage); + return; + } + + /// + /// Links a reference node to the project file. + /// + protected override void BindReferenceData() + { + Debug.Assert(!String.IsNullOrEmpty(this.referencedProjectName), "The referencedProjectName field has not been initialized"); + Debug.Assert(this.referencedProjectGuid != Guid.Empty, "The referencedProjectName field has not been initialized"); + + this.ItemNode = new ProjectElement(this.ProjectMgr, this.referencedProjectRelativePath, ProjectFileConstants.ProjectReference); + + this.ItemNode.SetMetadata(ProjectFileConstants.Name, this.referencedProjectName); + this.ItemNode.SetMetadata(ProjectFileConstants.Project, this.referencedProjectGuid.ToString("B")); + this.ItemNode.SetMetadata(ProjectFileConstants.Private, true.ToString()); + } + + /// + /// Defines whether this node is valid node for painting the refererence icon. + /// + /// + protected override bool CanShowDefaultIcon() + { + if(this.referencedProjectGuid == Guid.Empty || this.ProjectMgr == null || this.ProjectMgr.IsClosed || this.isNodeValid) + { + return false; + } + + IVsHierarchy hierarchy = null; + + hierarchy = VsShellUtilities.GetHierarchy(this.ProjectMgr.Site, this.referencedProjectGuid); + + if(hierarchy == null) + { + return false; + } + + //If the Project is unloaded return false + if(this.ReferencedProjectObject == null) + { + return false; + } + + return (!String.IsNullOrEmpty(this.referencedProjectFullPath) && File.Exists(this.referencedProjectFullPath)); + } + + /// + /// Checks if a project reference can be added to the hierarchy. It calls base to see if the reference is not already there, then checks for circular references. + /// + /// The error handler delegate to return + /// + protected override bool CanAddReference(out CannotAddReferenceErrorMessage errorHandler) + { + // When this method is called this refererence has not yet been added to the hierarchy, only instantiated. + if(!base.CanAddReference(out errorHandler)) + { + return false; + } + + errorHandler = null; + if(this.IsThisProjectReferenceInCycle()) + { + errorHandler = new CannotAddReferenceErrorMessage(ShowCircularReferenceErrorMessage); + return false; + } + + return true; + } + + private bool IsThisProjectReferenceInCycle() + { + return IsReferenceInCycle(this.referencedProjectGuid); + } + + private void ShowCircularReferenceErrorMessage() + { + string message = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.ProjectContainsCircularReferences, CultureInfo.CurrentUICulture), this.referencedProjectName); + string title = string.Empty; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + VsShellUtilities.ShowMessageBox(this.ProjectMgr.Site, title, message, icon, buttons, defaultButton); + } + + /// + /// Recursively search if this project reference guid is in cycle. + /// + private bool IsReferenceInCycle(Guid projectGuid) + { + IVsHierarchy hierarchy = VsShellUtilities.GetHierarchy(this.ProjectMgr.Site, projectGuid); + + IReferenceContainerProvider provider = hierarchy as IReferenceContainerProvider; + if(provider != null) + { + IReferenceContainer referenceContainer = provider.GetReferenceContainer(); + + Debug.Assert(referenceContainer != null, "Could not found the References virtual node"); + + foreach(ReferenceNode refNode in referenceContainer.EnumReferences()) + { + ProjectReferenceNode projRefNode = refNode as ProjectReferenceNode; + if(projRefNode != null) + { + if(projRefNode.ReferencedProjectGuid == this.ProjectMgr.ProjectIDGuid) + { + return true; + } + + if(this.IsReferenceInCycle(projRefNode.ReferencedProjectGuid)) + { + return true; + } + } + } + } + + return false; + } + #endregion + } + +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectSecurityChecker.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectSecurityChecker.cs new file mode 100644 index 0000000000..61a51cc5f3 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectSecurityChecker.cs @@ -0,0 +1,852 @@ +/*************************************************************************** + +Copyright (c) Microsoft Corporation. All rights reserved. +This code is licensed under the Visual Studio SDK license terms. +THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. + +***************************************************************************/ +extern alias Shell10; + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.Permissions; +using System.Text; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.Win32; + +namespace Microsoft.VisualStudio.Project +{ + using Url = Shell10.Microsoft.VisualStudio.Shell.Url; + + /// + /// Does security validation of a project before loading the project + /// + public class ProjectSecurityChecker : IDisposable + { + #region constants + /// + /// The dangereous target property. + /// + internal const string DangerousTargetProperty = "LoadTimeSensitiveTargets"; + + /// + /// The dangereous properties property. + /// + internal const string DangerousPropertyProperty = "LoadTimeSensitiveProperties"; + + /// + /// The dangereous items property. + /// + internal const string DangerousItemsProperty = "LoadTimeSensitiveItems"; + + /// + /// The check item locations property. + /// + internal const string CheckItemLocationProperty = "LoadTimeCheckItemLocation"; + + /// + /// The dangereous list item separator. + /// + internal const string DangerousListSeparator = ";"; + + /// + /// The project directory property. + /// + internal const string ProjectDirectoryProperty = "MSBuildProjectDirectory"; + + /// + /// The default dangereous properties. + /// + internal const string DefaultDangerousProperties = "LoadTimeSensitiveTargets;LoadTimeSensitiveProperties;LoadTimeSensitiveItems;LoadTimeCheckItemLocation;"; + + /// + /// The default dangereous targets. + /// + internal const string DefaultDangerousTargets = "Compile;GetFrameworkPaths;AllProjectOutputGroups;AllProjectOutputGroupsDependencies;CopyRunEnvironmentFiles;ResolveComReferences;ResolveAssemblyReferences;ResolveNativeReferences;"; + + /// + /// The default dangereous items. + /// + internal const string DefaultDangerousItems = ";"; + + /// + /// Defined the safe imports subkey in the registry. + /// + internal const string SafeImportsSubkey = @"MSBuild\SafeImports"; + #endregion + + #region fields + /// + /// Defines an object that will be a mutex for this object for synchronizing thread calls. + /// + private static volatile object Mutex = new object(); + + /// + /// Flag determining if the object has been disposed. + /// + private bool isDisposed; + + /// + /// The associated project shim for the project file + /// + private ProjectShim projectShim; + + /// + /// The security check helper object used to call out to do necessary security checkings. + /// + private SecurityCheckHelper securityCheckHelper = new SecurityCheckHelper(); + + /// + /// The associated service provider. + /// + private IServiceProvider serviceProvider; + + #endregion + + #region properties + /// + /// The associated project shim for the project file + /// + /// The project shim is made internal in order to be able to be passed to the user project. + internal protected ProjectShim ProjectShim + { + get { return this.projectShim; } + } + + /// + /// The security check helper that will be used to perform the necessary checkings. + /// + protected SecurityCheckHelper SecurityCheckHelper + { + get { return this.securityCheckHelper; } + } + + /// + /// The associated service provider. + /// + protected IServiceProvider ServiceProvider + { + get + { + return this.serviceProvider; + } + } + #endregion + + #region ctors + /// + /// Overloaded Constructor + /// + /// path to the project file + /// A service provider. + public ProjectSecurityChecker(IServiceProvider serviceProvider, string projectFilePath) + { + if(serviceProvider == null) + { + throw new ArgumentNullException("serviceProvider"); + } + + if(String.IsNullOrEmpty(projectFilePath)) + { + throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "projectFilePath"); + } + + this.serviceProvider = serviceProvider; + + // Instantiate a new project shim that we are going to use for security checkings. + EngineShim engine = new EngineShim(); + this.projectShim = engine.CreateNewProject(); + this.projectShim.Load(projectFilePath); + } + #endregion + + #region IDisposable Members + + /// + /// The IDispose interface Dispose method for disposing the object determinastically. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + + #region virtual methods + /// + /// Check if the project is safe at load/design time + /// + /// If the project is not safe contains an error message, describing the reason. + /// true if the project is safe, false otherwise + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", + Justification = "The error message needs to be an out parameter. We are following here the Try... method patterns.")] + public virtual bool IsProjectSafeAtLoadTime(out string securityErrorMessage) + { + securityErrorMessage = String.Empty; + + StringBuilder securityMessageMaker = new StringBuilder(); + int counter = 0; + string tempMessage; + + // STEP 1: Check direct imports. + if(!this.IsProjectSafeWithImports(out tempMessage)) + { + ProjectSecurityChecker.FormatMessage(securityMessageMaker, ++counter, tempMessage); + securityErrorMessage = tempMessage; + } + + // STEP 2: Check dangerous properties + if(!this.IsProjectSafeWithProperties(out tempMessage)) + { + ProjectSecurityChecker.FormatMessage(securityMessageMaker, ++counter, tempMessage); + securityErrorMessage = tempMessage; + } + + // STEP 3: Check dangerous targets + if(!this.IsProjectSafeWithTargets(out tempMessage)) + { + ProjectSecurityChecker.FormatMessage(securityMessageMaker, ++counter, tempMessage); + securityErrorMessage = tempMessage; + } + + // STEP 4: Check dangerous items + if(!this.IsProjectSafeWithItems(out tempMessage)) + { + ProjectSecurityChecker.FormatMessage(securityMessageMaker, ++counter, tempMessage); + securityErrorMessage = tempMessage; + } + + // STEP 5: Check UsingTask tasks + if(!this.IsProjectSafeWithUsingTasks(out tempMessage)) + { + ProjectSecurityChecker.FormatMessage(securityMessageMaker, ++counter, tempMessage); + securityErrorMessage = tempMessage; + } + + // STEP 6: Check for items defined within the LoadTimeCheckItemLocation, whether they are defined in safe locations + if(!this.CheckItemsLocation(out tempMessage)) + { + securityMessageMaker.AppendFormat(CultureInfo.CurrentCulture, "{0}: ", (++counter).ToString(CultureInfo.CurrentCulture)); + securityMessageMaker.AppendLine(tempMessage); + securityErrorMessage = tempMessage; + } + + if(counter > 1) + { + securityErrorMessage = securityMessageMaker.ToString(); + } + + return String.IsNullOrEmpty(securityErrorMessage); + } + + /// + /// Checks if the project is safe with imports. The project file is considered + /// unsafe if it contains any imports not registered in the safe import regkey. + /// + /// At return describes the reason why the projects is not considered safe. + /// true if the project is safe regarding imports. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", + Justification = "The error message needs to be an out parameter. We are following here the Try... method patterns.")] + protected virtual bool IsProjectSafeWithImports(out string securityErrorMessage) + { + securityErrorMessage = String.Empty; + + // Now get the directly imports and do the comparision. + string[] directImports = this.securityCheckHelper.GetDirectlyImportedProjects(this.projectShim); + if(directImports != null && directImports.Length > 0) + { + IList safeImportList = ProjectSecurityChecker.GetSafeImportList(); + + for(int i = 0; i < directImports.Length; i++) + { + string fileToCheck = directImports[i]; + if(!ProjectSecurityChecker.IsSafeImport(safeImportList, fileToCheck)) + { + using(RegistryKey root = Shell10.Microsoft.VisualStudio.Shell.VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_Configuration)) + { + securityErrorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.DetailsImport, CultureInfo.CurrentUICulture), Path.GetFileName(this.projectShim.FullFileName), fileToCheck, Path.Combine(root.Name, SafeImportsSubkey)); + } + + return false; + } + } + } + + return true; + } + + + + /// + /// Checks if the project is safe regarding properties. + /// + /// At return describes the reason why the projects is not considered safe. + /// true if the project has only safe properties. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", + Justification = "The error message needs to be an out parameter. We are following here the Try... method patterns.")] + protected virtual bool IsProjectSafeWithProperties(out string securityErrorMessage) + { + securityErrorMessage = String.Empty; + + // Now ask the security check heper for the safe properties. + string reasonForFailure; + bool isUserFile; + bool isProjectSafe = this.securityCheckHelper.IsProjectSafe(ProjectSecurityChecker.DangerousPropertyProperty, + ProjectSecurityChecker.DefaultDangerousProperties, + this.projectShim, + null, + SecurityCheckPass.Properties, + out reasonForFailure, + out isUserFile); + + if(!isProjectSafe) + { + securityErrorMessage = this.GetMessageString(reasonForFailure, SR.DetailsProperty); + } + + return isProjectSafe; + } + + /// + /// Checks if the project is safe regarding targets. + /// + /// At return describes the reason why the projects is not considered safe. + /// true if the project has only safe targets. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", + Justification = "The error message needs to be an out parameter. We are following here the Try... method patterns.")] + protected virtual bool IsProjectSafeWithTargets(out string securityErrorMessage) + { + securityErrorMessage = String.Empty; + + // Now ask the security check heper for the safe targets. + string reasonForFailure; + bool isUserFile; + bool isProjectSafe = this.securityCheckHelper.IsProjectSafe(ProjectSecurityChecker.DangerousTargetProperty, + ProjectSecurityChecker.DefaultDangerousTargets, + this.projectShim, + null, + SecurityCheckPass.Targets, + out reasonForFailure, + out isUserFile); + + if(!isProjectSafe) + { + securityErrorMessage = this.GetMessageString(reasonForFailure, SR.DetailsTarget); + } + + return isProjectSafe; + } + + /// + /// Checks if the project is safe regarding items. + /// + /// At return describes the reason why the projects is not considered safe. + /// true if the project has only safe items. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", + Justification = "The error message needs to be an out parameter. We are following here the Try... method patterns.")] + protected virtual bool IsProjectSafeWithItems(out string securityErrorMessage) + { + securityErrorMessage = String.Empty; + + // Now ask the security check heper for the safe items. + string reasonForFailure; + bool isUserFile; + bool isProjectSafe = this.securityCheckHelper.IsProjectSafe(ProjectSecurityChecker.DangerousItemsProperty, + ProjectSecurityChecker.DefaultDangerousItems, + this.projectShim, + null, + SecurityCheckPass.Items, + out reasonForFailure, + out isUserFile); + + if(!isProjectSafe) + { + securityErrorMessage = this.GetMessageString(reasonForFailure, SR.DetailsItem); + } + + return isProjectSafe; + } + + /// + /// Checks if the project is safe with using tasks. + /// + /// At return describes the reason why the projects is not considered safe. + /// true if the project has no using tasks defined in the project file. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", + Justification = "The error message needs to be an out parameter. We are following here the Try... method patterns.")] + protected virtual bool IsProjectSafeWithUsingTasks(out string securityErrorMessage) + { + securityErrorMessage = String.Empty; + + string[] usingTasks = this.securityCheckHelper.GetNonImportedUsingTasks(this.projectShim); + + if(usingTasks != null && usingTasks.Length > 0) + { + securityErrorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.DetailsUsingTask, CultureInfo.CurrentUICulture), Path.GetFileName(this.projectShim.FullFileName), usingTasks[0]); + return false; + } + + return true; + } + + /// + /// If the project contains the LoadTimeCheckItemsWithinProjectCone property, the method verifies that all the items listed in there are within the project cone. + /// Also checks that the project is not in Program Files or Windows if the property was there. + /// + /// At return describes the reason why the projects is not considered safe. + /// true if the project has no badly defined project items. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1021:AvoidOutParameters", MessageId = "0#", + Justification = "The error message needs to be an out parameter. We are following here the Try... method patterns.")] + protected virtual bool CheckItemsLocation(out string securityErrorMessage) + { + securityErrorMessage = String.Empty; + + // Get the property from the project + string itemLocationProperty = this.projectShim.GetEvaluatedProperty(ProjectSecurityChecker.CheckItemLocationProperty); + + if(String.IsNullOrEmpty(itemLocationProperty)) + { + return true; + } + + // Takes a semicolon separated list of entries, splits them and puts them into a list with values trimmed. + string[] items = itemLocationProperty.Split(ProjectSecurityChecker.DangerousListSeparator.ToCharArray(), StringSplitOptions.RemoveEmptyEntries); + + IList itemsToCheck = new List(); + foreach(string item in items) + { + itemsToCheck.Add(item.Trim()); + } + + // Now check the items for being defined in a safe location. + string reasonForFailure; + ItemSecurityChecker itemsSecurityChecker = new ItemSecurityChecker(this.serviceProvider, this.projectShim.FullFileName); + if(!itemsSecurityChecker.CheckItemsSecurity(this.projectShim, itemsToCheck, out reasonForFailure)) + { + securityErrorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.DetailsItemLocation, CultureInfo.CurrentUICulture), Path.GetFileName(this.projectShim.FullFileName), reasonForFailure); + return false; + } + + return true; + } + + + /// + /// The method that does the cleanup. + /// + /// true if called from IDispose.Dispose; false if called from Finalizer. + protected virtual void Dispose(bool disposing) + { + // Everybody can go here. + if(!this.isDisposed) + { + // Synchronize calls to the Dispose simultaniously. + lock(Mutex) + { + if(disposing) + { + this.projectShim.ParentEngine.UnloadProject(this.projectShim); + } + + this.isDisposed = true; + } + } + } + #endregion + + #region helper methods + /// + /// Gets a message string that has an associated format with a reason for failure. + /// + /// + /// + /// + internal string GetMessageString(string reasonForFailure, string resourceID) + { + Debug.Assert(!String.IsNullOrEmpty(reasonForFailure), "The reason for failure should not be empty or null"); + Debug.Assert(!String.IsNullOrEmpty(resourceID), "The resource id string cannot be empty"); + + return String.Format(CultureInfo.CurrentCulture, SR.GetString(resourceID, Path.GetFileName(this.projectShim.FullFileName), reasonForFailure)); + } + + /// + /// Generates a format string that will be pushed to the More Detailed dialog. + /// + /// The Stringbuilder object containing the formatted message. + /// The 'issue' number. + /// The message to format. + private static void FormatMessage(StringBuilder securityMessageMaker, int counter, string securityErrorMessage) + { + securityMessageMaker.AppendFormat(CultureInfo.CurrentCulture, "{0}: ", counter.ToString(CultureInfo.CurrentCulture)); + securityMessageMaker.AppendLine(securityErrorMessage); + securityMessageMaker.Append(Environment.NewLine); + } + + /// + /// Returns a set of file info's describing the files in the SafeImports registry location. + /// + /// A set of FileInfo objects describing the files in the SafeImports location. + private static IList GetSafeImportList() + { + List importsList = new List(); + + using(RegistryKey root = Shell10.Microsoft.VisualStudio.Shell.VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_Configuration)) + { + if(root != null) + { + using(RegistryKey key = root.OpenSubKey(SafeImportsSubkey)) + { + if(key != null) + { + foreach(string value in key.GetValueNames()) + { + string keyValue = key.GetValue(value, String.Empty, RegistryValueOptions.None) as string; + // Make sure that the environment variables are expanded. + keyValue = System.Environment.ExpandEnvironmentVariables(keyValue); + Uri uri; + if(!String.IsNullOrEmpty(keyValue) && Uri.TryCreate(keyValue, UriKind.Absolute, out uri) && uri.IsAbsoluteUri) + { + importsList.Add(keyValue); + } + } + } + } + } + } + + return importsList; + } + + /// + /// Checks if an import is a safe import. + /// + /// A list of safe imports from teh registry. + /// The file to check. + /// true if the file to check can be found in the safe import list + private static bool IsSafeImport(IList safeImportsList, string fileToCheck) + { + foreach(string safeImport in safeImportsList) + { + if(NativeMethods.IsSamePath(safeImport, fileToCheck)) + { + return true; + } + } + + return false; + } + #endregion + + #region nested types + /// + /// Class for checking that the items defined in LoadTimeCheckItemLocation are being defined in safe locations. + /// + private class ItemSecurityChecker + { + #region fields + /// + /// The associated service provider. + /// + private IServiceProvider serviceProvider; + + /// + /// The solutionFolder; + /// + private Uri solutionFolder; + + /// + /// The project folder + /// + private Uri projectFolder; + + /// + /// The set of special folders. + /// + private IList specialFolders; + #endregion + + #region ctors + /// + /// Overloaded Constructor + /// + /// path to the project file + /// A service provider. + internal ItemSecurityChecker(IServiceProvider serviceProvider, string projectFullPath) + { + this.serviceProvider = serviceProvider; + + // Initialize the project and solution folders. + this.SetProjectFolder(projectFullPath); + this.SetSolutionFolder(); + + // Set the special folders. Maybe this should be a static. + this.specialFolders = ItemSecurityChecker.SetSpecialFolders(); + } + #endregion + + #region methods + /// + /// Checks whether a set of project items described by the LoadTimeCheckItemLocation are in a safe location. + /// + /// The project shim containing the items to be checked. + /// The list of items to check if they are in the project cone. + /// The reason for failure if any of the files fails + /// true if all project items are in the project cone. Otherwise false. + internal bool CheckItemsSecurity(ProjectShim projectShim, IList itemsToCheck, out string reasonForFailure) + { + reasonForFailure = String.Empty; + + // If nothing to check assume that everything is ok. + if(itemsToCheck == null) + { + return true; + } + + Debug.Assert(projectShim != null, "Cannot check the items if no project has been defined!"); + + foreach(string itemName in itemsToCheck) + { + BuildItemGroupShim group = projectShim.GetEvaluatedItemsByNameIgnoringCondition(itemName); + if(group != null) + { + IEnumerator enumerator = group.GetEnumerator(); + while(enumerator.MoveNext()) + { + BuildItemShim item = enumerator.Current as BuildItemShim; + + string finalItem = item.FinalItemSpec; + + if(!String.IsNullOrEmpty(finalItem)) + { + // Perform the actual check - start with normalizing the path. Relative paths + // should be treated as relative to the project file. + string fullPath = this.GetFullPath(finalItem); + + // If the fullpath of the item is suspiciously short do not check it. + if(fullPath.Length >= 3) + { + Uri uri = null; + + // If we cannot create a uri from the item path return with the error + if(!Uri.TryCreate(fullPath, UriKind.Absolute, out uri)) + { + reasonForFailure = fullPath; + return false; + } + + // Check if the item points to a network share + if(uri.IsUnc) + { + reasonForFailure = fullPath; + return false; + } + + // Check if the item is located in a drive root directory + if(uri.Segments.Length == 3 && uri.Segments[1] == ":" && uri.Segments[2][0] == Path.DirectorySeparatorChar) + { + reasonForFailure = fullPath; + return false; + } + + //Check if the item is not in a special folder. + foreach(Uri specialFolder in this.specialFolders) + { + if(ItemSecurityChecker.IsItemInCone(uri, specialFolder)) + { + reasonForFailure = fullPath; + return false; + } + } + } + else + { + reasonForFailure = fullPath; + return false; + } + } + } + } + } + + return true; + } + + + /// + /// Gets the list of special directories. This method should be optimized if called more then once. + /// + /// The list of special directories + private static IList SetSpecialFolders() + { + string[] specialFolderArray = new string[5] + { + Environment.GetFolderPath(Environment.SpecialFolder.System), + Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), + Environment.GetFolderPath(Environment.SpecialFolder.Startup), + ItemSecurityChecker.GetSpecialDirectoryFromNative(NativeMethods.ExtendedSpecialFolder.Windows), + ItemSecurityChecker.GetSpecialDirectoryFromNative(NativeMethods.ExtendedSpecialFolder.CommonStartup) + }; + + List specialFolders = new List(5); + + // Add trailing backslash to the folders. + foreach(string specialFolder in specialFolderArray) + { + string tempFolder = specialFolder; + if(!tempFolder.EndsWith("\\", StringComparison.Ordinal)) + { + tempFolder += "\\"; + } + + specialFolders.Add(new Uri(tempFolder)); + } + + return specialFolders; + } + + /// + /// Some special folders are not supported by System.Environment.GetFolderPath. Get these special folders using p/invoke. + /// + /// The type of special folder to retrieve. + /// The folder path + private static string GetSpecialDirectoryFromNative(NativeMethods.ExtendedSpecialFolder extendedSpecialFolder) + { + string specialFolder = null; + IntPtr buffer = IntPtr.Zero; + + // Demand Unmanaged code permission. It should be normal to demand UnmanagedCodePermission from an assembly integrating into VS. + new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); + try + { + buffer = Marshal.AllocHGlobal((NativeMethods.MAX_PATH + 1) * 2); + IntPtr[] pathIdentifier = new IntPtr[1]; + + if(ErrorHandler.Succeeded(UnsafeNativeMethods.SHGetSpecialFolderLocation(IntPtr.Zero, (int)extendedSpecialFolder, pathIdentifier)) && UnsafeNativeMethods.SHGetPathFromIDList(pathIdentifier[0], buffer)) + { + specialFolder = Marshal.PtrToStringAuto(buffer); + } + } + finally + { + if(buffer != IntPtr.Zero) + { + Marshal.FreeHGlobal(buffer); + } + } + + + return specialFolder; + } + + + /// + /// Checks if the itemToCheck is in the cone of the baseUri. + /// + /// The item to check + /// The base to the item. This should define a folder. + /// true if the item to check is in the cone of the baseUri. + private static bool IsItemInCone(Uri itemToCheck, Uri baseUri) + { + Debug.Assert(itemToCheck != null && baseUri != null, "Cannot check for items since the input is wrong"); + Debug.Assert(!NativeMethods.IsSamePath(Path.GetDirectoryName(baseUri.LocalPath), baseUri.LocalPath), "The " + baseUri.LocalPath + " is not a folder!"); + + return (itemToCheck.IsFile && baseUri.IsFile && + String.Compare(itemToCheck.LocalPath, 0, baseUri.LocalPath, 0, baseUri.LocalPath.Length, StringComparison.OrdinalIgnoreCase) == 0); + } + + /// + /// Sets the solution folder. + /// + private void SetSolutionFolder() + { + if(this.solutionFolder != null) + { + return; + } + + IVsSolution solution = this.serviceProvider.GetService(typeof(SVsSolution)) as IVsSolution; + Debug.Assert(solution != null, "Could not retrieve the solution service from the global service provider"); + + string solutionDirectory, solutionFile, userOptionsFile; + + // We do not want to throw. If we cannot set the solution related constants we set them to empty string. + ErrorHandler.ThrowOnFailure(solution.GetSolutionInfo(out solutionDirectory, out solutionFile, out userOptionsFile)); + + if(String.IsNullOrEmpty(solutionDirectory)) + { + return; + } + + // Make sure the solution dir ends with a backslash + if(solutionDirectory[solutionDirectory.Length - 1] != Path.DirectorySeparatorChar) + { + solutionDirectory += Path.DirectorySeparatorChar; + } + + Uri.TryCreate(solutionDirectory, UriKind.Absolute, out this.solutionFolder); + + Debug.Assert(this.solutionFolder != null, "Could not create the Uri for the solution folder"); + } + + /// + /// Sets the project folder. + /// + /// The path to the project + private void SetProjectFolder(string projectFullPath) + { + if(this.projectFolder != null) + { + return; + } + + string tempProjectFolder = Path.GetDirectoryName(projectFullPath); + + // Make sure the project dir ends with a backslash + if(!tempProjectFolder.EndsWith("\\", StringComparison.Ordinal) && !tempProjectFolder.EndsWith("/", StringComparison.Ordinal)) + { + tempProjectFolder += "\\"; + } + + Uri.TryCreate(tempProjectFolder, UriKind.Absolute, out this.projectFolder); + + Debug.Assert(this.projectFolder != null, "Could not create the Uri for the project folder"); + } + + /// + /// Gets the fullpath of an item. + /// Relative pathes are treated as relative to the project file. + /// + /// The item. + /// The ful path of the item. + private string GetFullPath(string item) + { + Url url; + if(Path.IsPathRooted(item)) + { + // Use absolute path + url = new Url(item); + } + else + { + // Path is relative, so make it relative to project path + url = new Url(new Url(this.projectFolder.LocalPath), item); + } + + return url.AbsoluteUrl; + } + #endregion + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectShim.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectShim.cs new file mode 100644 index 0000000000..25c9422f1c --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ProjectShim.cs @@ -0,0 +1,94 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Build.BuildEngine; + +namespace Microsoft.VisualStudio.Project { + public class ProjectShim { + private readonly Microsoft.Build.BuildEngine.Project _project; + + public ProjectShim(Microsoft.Build.BuildEngine.Project project) { + _project = project; + } + + public string FullFileName { + get { + return _project.FullFileName; + } + } + + internal BuildItemGroupShim GetEvaluatedItemsByNameIgnoringCondition(string itemName) { + BuildItemGroup evaluatedItemsByNameIgnoringCondition = _project.GetEvaluatedItemsByNameIgnoringCondition(itemName); + if (evaluatedItemsByNameIgnoringCondition != null) { + return new BuildItemGroupShim(evaluatedItemsByNameIgnoringCondition); + } + return null; + } + + internal string GetEvaluatedProperty(string propertyName) { + return _project.GetEvaluatedProperty(propertyName); + } + + internal void Load(string projectFilePath) { + _project.Load(projectFilePath); + } + + internal EngineShim ParentEngine { + get { + Engine engine = _project.ParentEngine; + if (engine != null) { + return new EngineShim(engine); + } + return null; + } + } + + public Build.BuildEngine.Project Project { get { return _project; } } + + internal TargetCollectionShim Targets { + get { + TargetCollection targetCollection = this._project.Targets; + if (targetCollection != null) { + return new TargetCollectionShim(targetCollection); + } + return null; + } + } + + internal BuildPropertyGroupCollectionShim PropertyGroups { + get { + BuildPropertyGroupCollection buildPropertyGroupCollection = this._project.PropertyGroups; + if (buildPropertyGroupCollection != null) { + return new BuildPropertyGroupCollectionShim(buildPropertyGroupCollection); + } + return null; + } + } + + public UsingTaskCollection UsingTasks { + get { + return _project.UsingTasks; + } + } + + public ImportCollection Imports { + get { + return _project.Imports; + } + } + + internal BuildItemGroupCollectionShim ItemGroups { + get { + BuildItemGroupCollection buildItemGroupCollection = this._project.ItemGroups; + if (buildItemGroupCollection != null) { + return new BuildItemGroupCollectionShim(buildItemGroupCollection); + } + return null; + } + } + + + + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/PropertiesEditorLauncher.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/PropertiesEditorLauncher.cs new file mode 100644 index 0000000000..8647e24e05 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/PropertiesEditorLauncher.cs @@ -0,0 +1,64 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using ErrorHandler = Microsoft.VisualStudio.ErrorHandler; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// This class is used to enable launching the project properties + /// editor from the Properties Browser. + /// + [CLSCompliant(false)] + public class PropertiesEditorLauncher : ComponentEditor + { + private ServiceProvider serviceProvider; + + #region ctor + public PropertiesEditorLauncher(ServiceProvider serviceProvider) + { + if(serviceProvider == null) + throw new ArgumentNullException("serviceProvider"); + + this.serviceProvider = serviceProvider; + } + #endregion + #region overridden methods + /// + /// Launch the Project Properties Editor (properties pages) + /// + /// If we succeeded or not + public override bool EditComponent(ITypeDescriptorContext context, object component) + { + if(component is ProjectNodeProperties) + { + IVsPropertyPageFrame propertyPageFrame = (IVsPropertyPageFrame)serviceProvider.GetService((typeof(SVsPropertyPageFrame))); + + int hr = propertyPageFrame.ShowFrame(Guid.Empty); + if(ErrorHandler.Succeeded(hr)) + return true; + else + ErrorHandler.ThrowOnFailure(propertyPageFrame.ReportError(hr)); + } + + return false; + } + #endregion + + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ReferenceContainerNode.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ReferenceContainerNode.cs new file mode 100644 index 0000000000..5bc4ee4c68 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ReferenceContainerNode.cs @@ -0,0 +1,533 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Reflection; +using System.Runtime.InteropServices; +using System.Text; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using MSBuild = Microsoft.Build.Evaluation; +using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants; +using VsCommands = Microsoft.VisualStudio.VSConstants.VSStd97CmdID; +using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID; + +namespace Microsoft.VisualStudio.Project +{ + [CLSCompliant(false), ComVisible(true)] + public class ReferenceContainerNode : HierarchyNode, IReferenceContainer + { + #region fields + internal const string ReferencesNodeVirtualName = "References"; + #endregion + + #region ctor + public ReferenceContainerNode(ProjectNode root) + : base(root) + { + this.VirtualNodeName = ReferencesNodeVirtualName; + this.ExcludeNodeFromScc = true; + } + #endregion + + #region Properties + private static string[] supportedReferenceTypes = new string[] { + ProjectFileConstants.ProjectReference, + ProjectFileConstants.Reference, + ProjectFileConstants.COMReference + }; + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1819:PropertiesShouldNotReturnArrays")] + protected virtual string[] SupportedReferenceTypes + { + get { return supportedReferenceTypes; } + } + #endregion + + #region overridden properties + public override int SortPriority + { + get + { + return DefaultSortOrderNode.ReferenceContainerNode; + } + } + + public override int MenuCommandId + { + get { return VsMenus.IDM_VS_CTXT_REFERENCEROOT; } + } + + + public override Guid ItemTypeGuid + { + get { return VSConstants.GUID_ItemType_VirtualFolder; } + } + + + public override string Url + { + get { return this.VirtualNodeName; } + } + + public override string Caption + { + get + { + return SR.GetString(SR.ReferencesNodeName, CultureInfo.CurrentUICulture); + } + } + + + private Automation.OAReferences references; + internal override object Object + { + get + { + if(null == references) + { + references = new Automation.OAReferences(this); + } + return references; + } + } + + #endregion + + #region overridden methods + /// + /// Returns an instance of the automation object for ReferenceContainerNode + /// + /// An intance of the Automation.OAReferenceFolderItem type if succeeeded + public override object GetAutomationObject() + { + if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return null; + } + + return new Automation.OAReferenceFolderItem(this.ProjectMgr.GetAutomationObject() as Automation.OAProject, this); + } + + /// + /// Disable inline editing of Caption of a ReferendeContainerNode + /// + /// null + public override string GetEditLabel() + { + return null; + } + + + public override object GetIconHandle(bool open) + { + return this.ProjectMgr.ImageHandler.GetIconHandle(open ? (int)ProjectNode.ImageName.OpenReferenceFolder : (int)ProjectNode.ImageName.ReferenceFolder); + } + + + /// + /// References node cannot be dragged. + /// + /// A stringbuilder. + protected internal override StringBuilder PrepareSelectedNodesForClipBoard() + { + return null; + } + + /// + /// Not supported. + /// + protected override int ExcludeFromProject() + { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + protected override int QueryStatusOnNode(Guid cmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result) + { + if(cmdGroup == VsMenus.guidStandardCommandSet97) + { + switch((VsCommands)cmd) + { + case VsCommands.AddNewItem: + case VsCommands.AddExistingItem: + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + } + } + else if(cmdGroup == VsMenus.guidStandardCommandSet2K) + { + if((VsCommands2K)cmd == VsCommands2K.ADDREFERENCE) + { + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + } + } + else + { + return (int)OleConstants.OLECMDERR_E_UNKNOWNGROUP; + } + return base.QueryStatusOnNode(cmdGroup, cmd, pCmdText, ref result); + } + + protected override int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) + { + if(cmdGroup == VsMenus.guidStandardCommandSet2K) + { + switch((VsCommands2K)cmd) + { + case VsCommands2K.ADDREFERENCE: + return this.ProjectMgr.AddProjectReference(); + case VsCommands2K.ADDWEBREFERENCE: + return this.ProjectMgr.AddWebReference(); + } + } + + return base.ExecCommandOnNode(cmdGroup, cmd, nCmdexecopt, pvaIn, pvaOut); + } + + protected override bool CanDeleteItem(__VSDELETEITEMOPERATION deleteOperation) + { + return false; + } + + /// + /// Defines whether this node is valid node for painting the refererences icon. + /// + /// + protected override bool CanShowDefaultIcon() + { + if(!String.IsNullOrEmpty(this.VirtualNodeName)) + { + return true; + } + return false; + } + + #endregion + + #region IReferenceContainer + public IList EnumReferences() + { + List refs = new List(); + for(HierarchyNode node = this.FirstChild; node != null; node = node.NextSibling) + { + ReferenceNode refNode = node as ReferenceNode; + if(refNode != null) + { + refs.Add(refNode); + } + } + + return refs; + } + /// + /// Adds references to this container from a MSBuild project. + /// + public void LoadReferencesFromBuildProject(MSBuild.Project buildProject) + { + foreach(string referenceType in SupportedReferenceTypes) + { + IEnumerable refererncesGroup = this.ProjectMgr.BuildProject.GetItems(referenceType); + + bool isAssemblyReference = referenceType == ProjectFileConstants.Reference; + // If the project was loaded for browsing we should still create the nodes but as not resolved. + if(isAssemblyReference && this.ProjectMgr.Build(MsBuildTarget.ResolveAssemblyReferences) != MSBuildResult.Successful) + { + continue; + } + + foreach (MSBuild.ProjectItem item in refererncesGroup) + { + ProjectElement element = new ProjectElement(this.ProjectMgr, item, false); + + ReferenceNode node = CreateReferenceNode(referenceType, element); + + if(node != null) + { + // Make sure that we do not want to add the item twice to the ui hierarchy + // We are using here the UI representation of the Node namely the Caption to find that out, in order to + // avoid different representation problems. + // Example : + // + bool found = false; + for(HierarchyNode n = this.FirstChild; n != null && !found; n = n.NextSibling) + { + if(String.Compare(n.Caption, node.Caption, StringComparison.OrdinalIgnoreCase) == 0) + { + found = true; + } + } + + if(!found) + { + this.AddChild(node); + } + } + } + } + } + + /// + /// Adds a reference to this container using the selector data structure to identify it. + /// + /// data describing selected component + /// Reference in case of a valid reference node has been created. Otherwise null + public ReferenceNode AddReferenceFromSelectorData(VSCOMPONENTSELECTORDATA selectorData) + { + //Make sure we can edit the project file + if(!this.ProjectMgr.QueryEditProjectFile(false)) + { + throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + + //Create the reference node + ReferenceNode node = null; + try + { + node = CreateReferenceNode(selectorData); + } + catch(ArgumentException) + { + // Some selector data was not valid. + } + + //Add the reference node to the project if we have a valid reference node + if(node != null) + { + // This call will find if the reference is in the project and, in this case + // will not add it again, so the parent node will not be set. + node.AddReference(); + if(null == node.Parent) + { + // The reference was not added, so we can not return this item because it + // is not inside the project. + return null; + } + } + + return node; + } + #endregion + + #region virtual methods + protected virtual ReferenceNode CreateReferenceNode(string referenceType, ProjectElement element) + { + ReferenceNode node = null; + if(referenceType == ProjectFileConstants.COMReference) + { + node = this.CreateComReferenceNode(element); + } + else if(referenceType == ProjectFileConstants.Reference) + { + node = this.CreateAssemblyReferenceNode(element); + } + else if(referenceType == ProjectFileConstants.ProjectReference) + { + node = this.CreateProjectReferenceNode(element); + } + + return node; + } + + protected virtual ReferenceNode CreateReferenceNode(VSCOMPONENTSELECTORDATA selectorData) + { + ReferenceNode node = null; + switch(selectorData.type) + { + case VSCOMPONENTTYPE.VSCOMPONENTTYPE_Project: + node = this.CreateProjectReferenceNode(selectorData); + break; + case VSCOMPONENTTYPE.VSCOMPONENTTYPE_File: + // This is the case for managed assembly + case VSCOMPONENTTYPE.VSCOMPONENTTYPE_ComPlus: + node = this.CreateFileComponent(selectorData); + break; + case VSCOMPONENTTYPE.VSCOMPONENTTYPE_Com2: + node = this.CreateComReferenceNode(selectorData); + break; + } + + return node; + } + #endregion + + #region Helper functions to add references + /// + /// Creates a project reference node given an existing project element. + /// + protected virtual ProjectReferenceNode CreateProjectReferenceNode(ProjectElement element) + { + return new ProjectReferenceNode(this.ProjectMgr, element); + } + /// + /// Create a Project to Project reference given a VSCOMPONENTSELECTORDATA structure + /// + protected virtual ProjectReferenceNode CreateProjectReferenceNode(VSCOMPONENTSELECTORDATA selectorData) + { + return new ProjectReferenceNode(this.ProjectMgr, selectorData.bstrTitle, selectorData.bstrFile, selectorData.bstrProjRef); + } + + /// + /// Creates an assemby or com reference node given a selector data. + /// + protected virtual ReferenceNode CreateFileComponent(VSCOMPONENTSELECTORDATA selectorData) + { + if(null == selectorData.bstrFile) + { + throw new ArgumentNullException("selectorData"); + } + + // We have a path to a file, it could be anything + // First see if it is a managed assembly + bool tryToCreateAnAssemblyReference = true; + if(File.Exists(selectorData.bstrFile)) + { + try + { + // We should not load the assembly in the current appdomain. + // If we do not do it like that and we load the assembly in the current appdomain then the assembly cannot be unloaded again. + // The following problems might arose in that case. + // 1. Assume that a user is extending the MPF and his project is creating a managed assembly dll. + // 2. The user opens VS and creates a project and builds it. + // 3. Then the user opens VS creates another project and adds a reference to the previously built assembly. This will load the assembly in the appdomain had we been using Assembly.ReflectionOnlyLoadFrom. + // 4. Then he goes back to the first project modifies it an builds it. A build error is issued that the assembly is used. + + // GetAssemblyName is assured not to load the assembly. + tryToCreateAnAssemblyReference = (AssemblyName.GetAssemblyName(selectorData.bstrFile) != null); + } + catch(BadImageFormatException) + { + // We have found the file and it is not a .NET assembly; no need to try to + // load it again. + tryToCreateAnAssemblyReference = false; + } + catch(FileLoadException) + { + // We must still try to load from here because this exception is thrown if we want + // to add the same assembly refererence from different locations. + tryToCreateAnAssemblyReference = true; + } + } + + ReferenceNode node = null; + + if(tryToCreateAnAssemblyReference) + { + // This might be a candidate for an assembly reference node. Try to load it. + // CreateAssemblyReferenceNode will suppress BadImageFormatException if the node cannot be created. + node = this.CreateAssemblyReferenceNode(selectorData.bstrFile); + } + + // If no node has been created try to create a com reference node. + if(node == null) + { + if(!File.Exists(selectorData.bstrFile)) + { + return null; + } + node = this.CreateComReferenceNode(selectorData); + } + + return node; + } + + /// + /// Creates an assembly refernce node from a project element. + /// + protected virtual AssemblyReferenceNode CreateAssemblyReferenceNode(ProjectElement element) + { + AssemblyReferenceNode node = null; + try + { + node = new AssemblyReferenceNode(this.ProjectMgr, element); + } + catch(ArgumentNullException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch(FileNotFoundException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch(BadImageFormatException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch(FileLoadException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch(System.Security.SecurityException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + + return node; + } + /// + /// Creates an assembly reference node from a file path. + /// + protected virtual AssemblyReferenceNode CreateAssemblyReferenceNode(string fileName) + { + AssemblyReferenceNode node = null; + try + { + node = new AssemblyReferenceNode(this.ProjectMgr, fileName); + } + catch(ArgumentNullException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch(FileNotFoundException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch(BadImageFormatException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch(FileLoadException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + catch(System.Security.SecurityException e) + { + Trace.WriteLine("Exception : " + e.Message); + } + + return node; + } + + /// + /// Creates a com reference node from the project element. + /// + protected virtual ComReferenceNode CreateComReferenceNode(ProjectElement reference) + { + return new ComReferenceNode(this.ProjectMgr, reference); + } + /// + /// Creates a com reference node from a selector data. + /// + protected virtual ComReferenceNode CreateComReferenceNode(Microsoft.VisualStudio.Shell.Interop.VSCOMPONENTSELECTORDATA selectorData) + { + ComReferenceNode node = new ComReferenceNode(this.ProjectMgr, selectorData); + return node; + } + #endregion + + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/ReferenceNode.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/ReferenceNode.cs new file mode 100644 index 0000000000..8c709987d9 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/ReferenceNode.cs @@ -0,0 +1,331 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.Permissions; +using System.Text; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants; +using VsCommands2K = Microsoft.VisualStudio.VSConstants.VSStd2KCmdID; + +namespace Microsoft.VisualStudio.Project +{ + [CLSCompliant(false), ComVisible(true)] + public abstract class ReferenceNode : HierarchyNode + { + protected delegate void CannotAddReferenceErrorMessage(); + + #region ctors + /// + /// constructor for the ReferenceNode + /// + protected ReferenceNode(ProjectNode root, ProjectElement element) + : base(root, element) + { + this.ExcludeNodeFromScc = true; + } + + /// + /// constructor for the ReferenceNode + /// + protected ReferenceNode(ProjectNode root) + : base(root) + { + this.ExcludeNodeFromScc = true; + } + + #endregion + + #region overridden properties + public override int MenuCommandId + { + get { return VsMenus.IDM_VS_CTXT_REFERENCE; } + } + + public override Guid ItemTypeGuid + { + get { return Guid.Empty; } + } + + public override string Url + { + get + { + return String.Empty; + } + } + + public override string Caption + { + get + { + return String.Empty; + } + } + #endregion + + #region overridden methods + protected override NodeProperties CreatePropertiesObject() + { + return new ReferenceNodeProperties(this); + } + + /// + /// Get an instance of the automation object for ReferenceNode + /// + /// An instance of Automation.OAReferenceItem type if succeeded + public override object GetAutomationObject() + { + if(this.ProjectMgr == null || this.ProjectMgr.IsClosed) + { + return null; + } + + return new Automation.OAReferenceItem(this.ProjectMgr.GetAutomationObject() as Automation.OAProject, this); + } + + /// + /// Disable inline editing of Caption of a ReferendeNode + /// + /// null + public override string GetEditLabel() + { + return null; + } + + + public override object GetIconHandle(bool open) + { + int offset = (this.CanShowDefaultIcon() ? (int)ProjectNode.ImageName.Reference : (int)ProjectNode.ImageName.DanglingReference); + return this.ProjectMgr.ImageHandler.GetIconHandle(offset); + } + + /// + /// This method is called by the interface method GetMkDocument to specify the item moniker. + /// + /// The moniker for this item + public override string GetMkDocument() + { + return this.Url; + } + + /// + /// Not supported. + /// + protected override int ExcludeFromProject() + { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + /// + /// References node cannot be dragged. + /// + /// A stringbuilder. + protected internal override StringBuilder PrepareSelectedNodesForClipBoard() + { + return null; + } + + protected override int QueryStatusOnNode(Guid cmdGroup, uint cmd, IntPtr pCmdText, ref QueryStatusResult result) + { + if(cmdGroup == VsMenus.guidStandardCommandSet2K) + { + if((VsCommands2K)cmd == VsCommands2K.QUICKOBJECTSEARCH) + { + result |= QueryStatusResult.SUPPORTED | QueryStatusResult.ENABLED; + return VSConstants.S_OK; + } + } + else + { + return (int)OleConstants.OLECMDERR_E_UNKNOWNGROUP; + } + return base.QueryStatusOnNode(cmdGroup, cmd, pCmdText, ref result); + } + + protected override int ExecCommandOnNode(Guid cmdGroup, uint cmd, uint nCmdexecopt, IntPtr pvaIn, IntPtr pvaOut) + { + if(cmdGroup == VsMenus.guidStandardCommandSet2K) + { + if((VsCommands2K)cmd == VsCommands2K.QUICKOBJECTSEARCH) + { + return this.ShowObjectBrowser(); + } + } + + return base.ExecCommandOnNode(cmdGroup, cmd, nCmdexecopt, pvaIn, pvaOut); + } + + #endregion + + #region methods + + + /// + /// Links a reference node to the project and hierarchy. + /// + public virtual void AddReference() + { + ReferenceContainerNode referencesFolder = this.ProjectMgr.FindChild(ReferenceContainerNode.ReferencesNodeVirtualName) as ReferenceContainerNode; + Debug.Assert(referencesFolder != null, "Could not find the References node"); + + CannotAddReferenceErrorMessage referenceErrorMessageHandler = null; + + if(!this.CanAddReference(out referenceErrorMessageHandler)) + { + if(referenceErrorMessageHandler != null) + { + referenceErrorMessageHandler.DynamicInvoke(new object[] { }); + } + return; + } + + // Link the node to the project file. + this.BindReferenceData(); + + // At this point force the item to be refreshed + this.ItemNode.RefreshProperties(); + + referencesFolder.AddChild(this); + + return; + } + + /// + /// Refreshes a reference by re-resolving it and redrawing the icon. + /// + internal virtual void RefreshReference() + { + this.ResolveReference(); + this.ReDraw(UIHierarchyElement.Icon); + } + + /// + /// Resolves references. + /// + protected virtual void ResolveReference() + { + + } + + /// + /// Validates that a reference can be added. + /// + /// A CannotAddReferenceErrorMessage delegate to show the error message. + /// true if the reference can be added. + protected virtual bool CanAddReference(out CannotAddReferenceErrorMessage errorHandler) + { + // When this method is called this refererence has not yet been added to the hierarchy, only instantiated. + errorHandler = null; + if(this.IsAlreadyAdded()) + { + return false; + } + + return true; + } + + + /// + /// Checks if a reference is already added. The method parses all references and compares the Url. + /// + /// true if the assembly has already been added. + protected virtual bool IsAlreadyAdded() + { + ReferenceContainerNode referencesFolder = this.ProjectMgr.FindChild(ReferenceContainerNode.ReferencesNodeVirtualName) as ReferenceContainerNode; + Debug.Assert(referencesFolder != null, "Could not find the References node"); + + for(HierarchyNode n = referencesFolder.FirstChild; n != null; n = n.NextSibling) + { + ReferenceNode refererenceNode = n as ReferenceNode; + if(null != refererenceNode) + { + // We check if the Url of the assemblies is the same. + if(NativeMethods.IsSamePath(refererenceNode.Url, this.Url)) + { + return true; + } + } + } + + return false; + } + + + /// + /// Shows the Object Browser + /// + /// + protected virtual int ShowObjectBrowser() + { + if(String.IsNullOrEmpty(this.Url) || !File.Exists(this.Url)) + { + return (int)OleConstants.OLECMDERR_E_NOTSUPPORTED; + } + + // Request unmanaged code permission in order to be able to creaet the unmanaged memory representing the guid. + new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); + + Guid guid = VSConstants.guidCOMPLUSLibrary; + IntPtr ptr = System.Runtime.InteropServices.Marshal.AllocCoTaskMem(guid.ToByteArray().Length); + + System.Runtime.InteropServices.Marshal.StructureToPtr(guid, ptr, false); + int returnValue = VSConstants.S_OK; + try + { + VSOBJECTINFO[] objInfo = new VSOBJECTINFO[1]; + + objInfo[0].pguidLib = ptr; + objInfo[0].pszLibName = this.Url; + + IVsObjBrowser objBrowser = this.ProjectMgr.Site.GetService(typeof(SVsObjBrowser)) as IVsObjBrowser; + + ErrorHandler.ThrowOnFailure(objBrowser.NavigateTo(objInfo, 0)); + } + catch(COMException e) + { + Trace.WriteLine("Exception" + e.ErrorCode); + returnValue = e.ErrorCode; + } + finally + { + if(ptr != IntPtr.Zero) + { + System.Runtime.InteropServices.Marshal.FreeCoTaskMem(ptr); + } + } + + return returnValue; + } + + protected override bool CanDeleteItem(__VSDELETEITEMOPERATION deleteOperation) + { + if(deleteOperation == __VSDELETEITEMOPERATION.DELITEMOP_RemoveFromProject) + { + return true; + } + return false; + } + + protected abstract void BindReferenceData(); + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/RegisteredProjectType.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/RegisteredProjectType.cs new file mode 100644 index 0000000000..407c068da1 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/RegisteredProjectType.cs @@ -0,0 +1,160 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.Win32; +using VSRegistry = Microsoft.VisualStudio.Shell.VSRegistry; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Gets registry settings from for a project. + /// + internal class RegisteredProjectType + { + private string defaultProjectExtension; + + private string projectTemplatesDir; + + private string wizardTemplatesDir; + + private Guid packageGuid; + + internal const string DefaultProjectExtension = "DefaultProjectExtension"; + internal const string WizardsTemplatesDir = "WizardsTemplatesDir"; + internal const string ProjectTemplatesDir = "ProjectTemplatesDir"; + internal const string Package = "Package"; + + + + internal string DefaultProjectExtensionValue + { + get + { + return this.defaultProjectExtension; + } + set + { + this.defaultProjectExtension = value; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal string ProjectTemplatesDirValue + { + get + { + return this.projectTemplatesDir; + } + set + { + this.projectTemplatesDir = value; + } + } + + internal string WizardTemplatesDirValue + { + get + { + return this.wizardTemplatesDir; + } + set + { + this.wizardTemplatesDir = value; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal Guid PackageGuidValue + { + get + { + return this.packageGuid; + } + set + { + this.packageGuid = value; + } + } + + /// + /// If the project support VsTemplates, returns the path to + /// the vstemplate file corresponding to the requested template + /// + /// You can pass in a string such as: "Windows\Console Application" + /// + internal string GetVsTemplateFile(string templateFile) + { + // First see if this use the vstemplate model + if(!String.IsNullOrEmpty(DefaultProjectExtensionValue)) + { + EnvDTE80.DTE2 dte = Microsoft.VisualStudio.Shell.Package.GetGlobalService(typeof(EnvDTE.DTE)) as EnvDTE80.DTE2; + if(dte != null) + { + EnvDTE80.Solution2 solution = dte.Solution as EnvDTE80.Solution2; + if(solution != null) + { + string fullPath = solution.GetProjectTemplate(templateFile, DefaultProjectExtensionValue); + // The path returned by GetProjectTemplate can be in the format "path|FrameworkVersion=x.y|Language=xxx" + // where the framework version and language sections are optional. + // Here we are interested only in the full path, so we have to remove all the other sections. + int pipePos = fullPath.IndexOf('|'); + if(0 == pipePos) + { + return null; + } + if(pipePos > 0) + { + fullPath = fullPath.Substring(0, pipePos); + } + return fullPath; + } + } + + } + return null; + } + + internal static RegisteredProjectType CreateRegisteredProjectType(Guid projectTypeGuid) + { + RegisteredProjectType registederedProjectType = null; + + using(RegistryKey rootKey = VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_Configuration)) + { + if(rootKey == null) + { + return null; + } + + string projectPath = "Projects\\" + projectTypeGuid.ToString("B"); + using(RegistryKey projectKey = rootKey.OpenSubKey(projectPath)) + { + if(projectKey == null) + { + return null; + } + + registederedProjectType = new RegisteredProjectType(); + registederedProjectType.DefaultProjectExtensionValue = projectKey.GetValue(DefaultProjectExtension) as string; + registederedProjectType.ProjectTemplatesDirValue = projectKey.GetValue(ProjectTemplatesDir) as string; + registederedProjectType.WizardTemplatesDirValue = projectKey.GetValue(WizardsTemplatesDir) as string; + registederedProjectType.PackageGuidValue = new Guid(projectKey.GetValue(Package) as string); + } + } + + return registederedProjectType; + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Resources/imagelis.bmp b/Tools/IronStudio/IronStudio/VisualStudio/Project/Resources/imagelis.bmp new file mode 100644 index 0000000000..43cb71c75b Binary files /dev/null and b/Tools/IronStudio/IronStudio/VisualStudio/Project/Resources/imagelis.bmp differ diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/SecurityCheckHelper.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/SecurityCheckHelper.cs new file mode 100644 index 0000000000..ec60675543 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/SecurityCheckHelper.cs @@ -0,0 +1,143 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Build.BuildEngine; + +namespace Microsoft.VisualStudio.Project { + public class SecurityCheckHelper { + public string[] GetNonImportedUsingTasks(ProjectShim project) { + List list = new List(); + foreach (UsingTask task in project.UsingTasks) { + if (!task.IsImported) { + list.Add(task.TaskName); + } + } + return list.ToArray(); + } + + public string[] GetDirectlyImportedProjects(ProjectShim project) { + List list = new List(); + foreach (Import import in project.Imports) { + if (!import.IsImported) { + list.Add(import.EvaluatedProjectPath); + } + } + return list.ToArray(); + } + + internal bool IsProjectSafe(string dangerousItemsPropertyName, string defaultDangerousItems, ProjectShim mainProject, ProjectShim userProject, SecurityCheckPass pass, out string reasonFailed, out bool isUserFile) { + reasonFailed = string.Empty; + isUserFile = false; + string[] nonImportedItems = null; + string[] nonImportedTargetNames = null; + switch (pass) { + case SecurityCheckPass.Targets: + if (mainProject != null) { + nonImportedItems = GetNonImportedTargetNames(mainProject); + } + if (userProject != null) { + nonImportedTargetNames = GetNonImportedTargetNames(userProject); + } + break; + + case SecurityCheckPass.Properties: + if (mainProject != null) { + nonImportedItems = GetNonImportedPropertyNames(mainProject); + } + if (userProject != null) { + nonImportedTargetNames = GetNonImportedPropertyNames(userProject); + } + break; + + case SecurityCheckPass.Items: + if (mainProject != null) { + nonImportedItems = GetNonImportedItemNames(mainProject); + } + if (userProject != null) { + nonImportedTargetNames = GetNonImportedItemNames(userProject); + } + break; + + default: + return false; + } + Dictionary dangerousItems = CreateDangerousItemHashtable(defaultDangerousItems + mainProject.GetEvaluatedProperty(dangerousItemsPropertyName)); + bool flag = IsProjectSafeHelper(nonImportedItems, dangerousItems, out reasonFailed); + if (!flag) { + isUserFile = false; + return false; + } + bool flag2 = IsProjectSafeHelper(nonImportedTargetNames, dangerousItems, out reasonFailed); + if (!flag2) { + isUserFile = true; + return false; + } + return (flag && flag2); + } + + internal static string[] GetNonImportedItemNames(ProjectShim project) { + Dictionary hashtable = new Dictionary(); + foreach (BuildItemGroupShim shim in project.ItemGroups) { + if (!shim.IsImported) { + foreach (BuildItemShim shim2 in shim) { + hashtable[shim2.Name] = string.Empty; + } + continue; + } + } + return hashtable.Keys.ToArray(); + } + + + internal static string[] GetNonImportedTargetNames(ProjectShim project) { + List list = new List(); + foreach (TargetShim shim in project.Targets) { + if (!shim.IsImported) { + list.Add(shim.Name); + } + } + return list.ToArray(); + } + + internal static string[] GetNonImportedPropertyNames(ProjectShim project) { + Dictionary hashtable = new Dictionary(); + foreach (BuildPropertyGroupShim shim in project.PropertyGroups) { + if (!shim.IsImported) { + foreach (BuildPropertyShim shim2 in shim) { + hashtable[shim2.Name] = string.Empty; + } + continue; + } + } + return hashtable.Keys.ToArray(); + } + + internal static Dictionary CreateDangerousItemHashtable(string dangerousItems) { + Dictionary dictionary = new Dictionary(StringComparer.OrdinalIgnoreCase); + if (!string.IsNullOrEmpty(dangerousItems)) { + foreach (string str in dangerousItems.Split(new char[] { ';' })) { + string str2 = str.Trim(); + if (str2.Length > 0) { + dictionary[str2] = string.Empty; + } + } + } + return dictionary; + } + + internal static bool IsProjectSafeHelper(string[] nonImportedItems, Dictionary dangerousItems, out string reasonFailed) { + if (nonImportedItems != null) { + foreach (string str in nonImportedItems) { + if (!string.IsNullOrEmpty(str) && (dangerousItems.ContainsKey(str) || (str[0] == '_'))) { + reasonFailed = str; + return false; + } + } + } + reasonFailed = string.Empty; + return true; + } + + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/SecurityWarningDialog.Designer.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/SecurityWarningDialog.Designer.cs new file mode 100644 index 0000000000..81587ee6dd --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/SecurityWarningDialog.Designer.cs @@ -0,0 +1,150 @@ +namespace Microsoft.VisualStudio.Project +{ + partial class SecurityWarningDialog + { + /// + /// Required designer variable. + /// + private System.ComponentModel.IContainer components = null; + + /// + /// Clean up any resources being used. + /// + /// true if managed resources should be disposed; otherwise, false. + protected override void Dispose(bool disposing) + { + if(disposing && (components != null)) + { + components.Dispose(); + } + base.Dispose(disposing); + } + + #region Windows Form Designer generated code + + /// + /// Required method for Designer support - do not modify + /// the contents of this method with the code editor. + /// + private void InitializeComponent() + { + System.ComponentModel.ComponentResourceManager resources = new System.ComponentModel.ComponentResourceManager(typeof(SecurityWarningDialog)); + this.warningText = new System.Windows.Forms.Label(); + this.detailsButton = new System.Windows.Forms.Button(); + this.optionBox = new System.Windows.Forms.GroupBox(); + this.loadButton = new System.Windows.Forms.RadioButton(); + this.browseButton = new System.Windows.Forms.RadioButton(); + this.loadText = new System.Windows.Forms.Label(); + this.browseText = new System.Windows.Forms.Label(); + this.askAgainCheckBox = new System.Windows.Forms.CheckBox(); + this.okButton = new System.Windows.Forms.Button(); + this.cancelButton = new System.Windows.Forms.Button(); + this.optionBox.SuspendLayout(); + this.SuspendLayout(); + // + // warningText + // + resources.ApplyResources(this.warningText, "warningText"); + this.warningText.Name = "warningText"; + // + // detailsButton + // + resources.ApplyResources(this.detailsButton, "detailsButton"); + this.detailsButton.Name = "detailsButton"; + this.detailsButton.UseVisualStyleBackColor = true; + this.detailsButton.Click += new System.EventHandler(this.detailsButton_Click); + // + // optionBox + // + this.optionBox.Controls.Add(this.loadButton); + this.optionBox.Controls.Add(this.browseButton); + this.optionBox.Controls.Add(this.loadText); + this.optionBox.Controls.Add(this.browseText); + resources.ApplyResources(this.optionBox, "optionBox"); + this.optionBox.Name = "optionBox"; + this.optionBox.TabStop = false; + // + // loadButton + // + resources.ApplyResources(this.loadButton, "loadButton"); + this.loadButton.Name = "loadButton"; + this.loadButton.TabStop = true; + this.loadButton.UseVisualStyleBackColor = true; + // + // browseButton + // + resources.ApplyResources(this.browseButton, "browseButton"); + this.browseButton.Name = "browseButton"; + this.browseButton.TabStop = true; + this.browseButton.UseVisualStyleBackColor = true; + // + // loadText + // + resources.ApplyResources(this.loadText, "loadText"); + this.loadText.Name = "loadText"; + // + // browseText + // + resources.ApplyResources(this.browseText, "browseText"); + this.browseText.Name = "browseText"; + // + // askAgainCheckBox + // + resources.ApplyResources(this.askAgainCheckBox, "askAgainCheckBox"); + this.askAgainCheckBox.Name = "askAgainCheckBox"; + this.askAgainCheckBox.UseVisualStyleBackColor = true; + // + // okButton + // + this.okButton.DialogResult = System.Windows.Forms.DialogResult.OK; + resources.ApplyResources(this.okButton, "okButton"); + this.okButton.Name = "okButton"; + this.okButton.UseVisualStyleBackColor = true; + this.okButton.Click += new System.EventHandler(this.okButton_Click); + // + // cancelButton + // + this.cancelButton.DialogResult = System.Windows.Forms.DialogResult.Cancel; + resources.ApplyResources(this.cancelButton, "cancelButton"); + this.cancelButton.Name = "cancelButton"; + this.cancelButton.UseVisualStyleBackColor = true; + this.cancelButton.Click += new System.EventHandler(this.cancelButton_Click); + // + // SecurityWarningDialog + // + this.AcceptButton = this.okButton; + resources.ApplyResources(this, "$this"); + this.AutoScaleMode = System.Windows.Forms.AutoScaleMode.Font; + this.CancelButton = this.cancelButton; + this.Controls.Add(this.cancelButton); + this.Controls.Add(this.okButton); + this.Controls.Add(this.askAgainCheckBox); + this.Controls.Add(this.optionBox); + this.Controls.Add(this.detailsButton); + this.Controls.Add(this.warningText); + this.FormBorderStyle = System.Windows.Forms.FormBorderStyle.FixedDialog; + this.MaximizeBox = false; + this.MinimizeBox = false; + this.Name = "SecurityWarningDialog"; + this.ShowInTaskbar = false; + this.optionBox.ResumeLayout(false); + this.optionBox.PerformLayout(); + this.ResumeLayout(false); + this.PerformLayout(); + + } + + #endregion + + private System.Windows.Forms.Label warningText; + private System.Windows.Forms.Button detailsButton; + private System.Windows.Forms.GroupBox optionBox; + private System.Windows.Forms.Label browseText; + private System.Windows.Forms.Label loadText; + private System.Windows.Forms.RadioButton browseButton; + private System.Windows.Forms.RadioButton loadButton; + private System.Windows.Forms.CheckBox askAgainCheckBox; + private System.Windows.Forms.Button okButton; + private System.Windows.Forms.Button cancelButton; + } +} \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/SecurityWarningDialog.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/SecurityWarningDialog.cs new file mode 100644 index 0000000000..50cedae804 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/SecurityWarningDialog.cs @@ -0,0 +1,218 @@ +/*************************************************************************** + +Copyright (c) Microsoft Corporation. All rights reserved. +This code is licensed under the Visual Studio SDK license terms. +THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. + +***************************************************************************/ + +using System; +using System.Globalization; +using System.IO; +using System.Windows.Forms; +using System.Windows.Forms.Design; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio; + +namespace Microsoft.VisualStudio.Project +{ + internal partial class SecurityWarningDialog : Form + { + #region fields + /// + /// The associated service provider + /// + private IServiceProvider serviceProvider; + + /// + /// The dialog message to be presented when the 'More' button is pressed. + /// + private string dialogMessage; + + /// + /// Teh full path to teh project. + /// + private string projectFullPath; + + /// + /// The value of the ask again check box. + /// + private bool askAgainCheckBoxValue; + + /// + /// The project load option the userwill choose on this form. + /// + private ProjectLoadOption projectLoadOption = ProjectLoadOption.DonNotLoad; + #endregion + + #region properties + /// + /// The value of the ask again check box. + /// + internal bool AskAgainCheckBoxValue + { + get + { + return this.askAgainCheckBoxValue; + } + } + + /// + /// The project load option the user has chosen to perform. + /// + internal ProjectLoadOption ProjectLoadOption + { + get + { + return this.projectLoadOption; + } + } + #endregion + + #region ctors + /// + /// Overloaded ctor. + /// + /// The associated service provider. + /// The message that will be shown when the 'More' button is pressed. + /// The full path of the project. + public SecurityWarningDialog(IServiceProvider serviceProvider, string dialogMessage, string projectFullpath) + { + if(serviceProvider == null) + { + throw new ArgumentNullException("serviceProvider"); + } + + if(String.IsNullOrEmpty(projectFullpath)) + { + throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "projectFullpath"); + } + + this.serviceProvider = serviceProvider; + + this.projectFullPath = projectFullpath; + + this.dialogMessage = dialogMessage; + + this.InitializeComponent(); + + this.SetupComponents(); + } + #endregion + + #region helpers + /// + /// Shows the dialog if possible hosted by the IUIService. + /// + /// A DialogResult + internal new DialogResult ShowDialog() + { + IUIService uiService = this.serviceProvider.GetService(typeof(IUIService)) as IUIService; + if(uiService == null) + { + return this.ShowDialog(); + } + + return uiService.ShowDialog(this); + } + + /// + /// Sets up the different UI elements. + /// + private void SetupComponents() + { + // Get the project name. + string projectName = Path.GetFileNameWithoutExtension(this.projectFullPath); + + IVsUIShell shell = this.serviceProvider.GetService(typeof(SVsUIShell)) as IVsUIShell; + + if(shell == null) + { + throw new InvalidOperationException(); + } + + String applicationName; + + // Get the name of the SKU. + ErrorHandler.ThrowOnFailure(shell.GetAppName(out applicationName)); + + // Set the dialog box caption (title). + this.Text = String.Format(CultureInfo.CurrentCulture, this.Text, projectName); + + // Set the text at the top of the dialog that gives a brief description of the security + // implications of loading this project. + this.warningText.Text = String.Format(CultureInfo.CurrentCulture, this.warningText.Text, projectName, applicationName); + + // Set the text that describes the "Browse" option. + this.browseText.Text = String.Format(CultureInfo.CurrentCulture, this.browseText.Text, applicationName); + + // Set the text that describes the "Load" option. + this.loadText.Text = String.Format(CultureInfo.CurrentCulture, this.loadText.Text, applicationName); + + // The default selection is "Browse" so select that radio button. + this.browseButton.Checked = true; + + // Turn on the "Ask me always" checkbox by default. + this.askAgainCheckBox.Checked = true; + + // Set the focus to the Browse button, so hitting Enter will press the OK button + this.browseButton.Focus(); + + this.CenterToScreen(); + } + + /// + /// The Cancel button was clicked. + /// + /// The sender of the event + /// An event arg Associated to the event. + private void cancelButton_Click(object sender, EventArgs e) + { + // In case the user presses the Cancel button, we assume he never wants to see this dialog again + // and pretend the "Ask me every time" checkbox is unchecked even if it is really checked. + this.askAgainCheckBoxValue = false; + this.projectLoadOption = ProjectLoadOption.DonNotLoad; + } + + /// + /// The OK button was clicked. + /// + /// The sender of the event + /// An event arg Associated to the event. + private void okButton_Click(object sender, EventArgs e) + { + if(this.browseButton.Checked && !this.loadButton.Checked) + { + this.projectLoadOption = ProjectLoadOption.LoadOnlyForBrowsing; + } + else + { + this.projectLoadOption = ProjectLoadOption.LoadNormally; + } + + this.askAgainCheckBoxValue = this.askAgainCheckBox.Checked; + + this.Close(); + } + + /// + /// Loads a messagebox explaining in detail the security problem + /// + /// The sender of the event + /// An event arg Associated to the event. + private void detailsButton_Click(object sender, EventArgs e) + { + string title = null; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_INFO; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + VsShellUtilities.ShowMessageBox(this.serviceProvider, title, this.dialogMessage, icon, buttons, defaultButton); + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/SecurityWarningDialog.resx b/Tools/IronStudio/IronStudio/VisualStudio/Project/SecurityWarningDialog.resx new file mode 100644 index 0000000000..d8f7470e08 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/SecurityWarningDialog.resx @@ -0,0 +1,449 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + + Microsoft Sans Serif, 8.25pt + + + 7, 7 + + + 488, 39 + + + + 0 + + + The {0} project file has been customized and could present a security risk by executing custom build steps when opened in {1}. If this project came from an untrustworthy source, it could cause damage to your computer or compromise your private information. + + + warningText + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 5 + + + Microsoft Sans Serif, 8.25pt + + + 12, 65 + + + 74, 23 + + + 1 + + + &More Details + + + detailsButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 4 + + + loadButton + + + System.Windows.Forms.RadioButton, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + optionBox + + + 0 + + + browseButton + + + System.Windows.Forms.RadioButton, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + optionBox + + + 1 + + + loadText + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + optionBox + + + 2 + + + browseText + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + optionBox + + + 3 + + + Microsoft Sans Serif, 8.25pt + + + 10, 103 + + + 488, 176 + + + 2 + + + Project load options + + + optionBox + + + System.Windows.Forms.GroupBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 3 + + + True + + + 6, 100 + + + 125, 17 + + + 3 + + + &Load project normally + + + loadButton + + + System.Windows.Forms.RadioButton, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + optionBox + + + 0 + + + True + + + 6, 20 + + + 144, 17 + + + 2 + + + Load project for &browsing + + + browseButton + + + System.Windows.Forms.RadioButton, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + optionBox + + + 1 + + + 20, 120 + + + 462, 53 + + + 1 + + + Opens the project normally in {0}. Use this option if you trust the source and understand the potential risks involved. {0} does not restrict any project functionality and will not prompt you again for this project. + + + loadText + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + optionBox + + + 2 + + + 20, 40 + + + 454, 47 + + + 0 + + + Opens the project in {0} with increased security. This option allows you to browse the contents of the project, but some functionality, such as IntelliSense, is restricted. When a project is loaded for browsing, actions such as building, cleaning, publishing, or opening designers could still remain unsafe. + + + browseText + + + System.Windows.Forms.Label, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + optionBox + + + 3 + + + True + + + 10, 295 + + + 209, 17 + + + 3 + + + &Ask me for every project in this solution + + + askAgainCheckBox + + + System.Windows.Forms.CheckBox, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 2 + + + 327, 328 + + + 75, 23 + + + 4 + + + OK + + + okButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 1 + + + 420, 328 + + + 75, 23 + + + 5 + + + Cancel + + + cancelButton + + + System.Windows.Forms.Button, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + $this + + + 0 + + + True + + + 6, 13 + + + 507, 363 + + + Microsoft Sans Serif, 8.25pt + + + Security Warning for {0} + + + SecurityWarningDialog + + + System.Windows.Forms.Form, System.Windows.Forms, Version=2.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/SelectionListener.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/SelectionListener.cs new file mode 100644 index 0000000000..5e62c879df --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/SelectionListener.cs @@ -0,0 +1,148 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using ShellConstants = Microsoft.VisualStudio.Shell.Interop.Constants; + +namespace Microsoft.VisualStudio.Project +{ + + [CLSCompliant(false)] + public abstract class SelectionListener : IVsSelectionEvents, IDisposable + { + #region fields + private uint eventsCookie; + private IVsMonitorSelection monSel; + private ServiceProvider serviceProvider; + private bool isDisposed; + /// + /// Defines an object that will be a mutex for this object for synchronizing thread calls. + /// + private static volatile object Mutex = new object(); + #endregion + + #region ctors + protected SelectionListener(ServiceProvider serviceProviderParameter) + { + if (serviceProviderParameter == null) + { + throw new ArgumentNullException("serviceProviderParameter"); + } + + this.serviceProvider = serviceProviderParameter; + this.monSel = this.serviceProvider.GetService(typeof(SVsShellMonitorSelection)) as IVsMonitorSelection; + + if(this.monSel == null) + { + throw new InvalidOperationException(); + } + } + #endregion + + #region properties + protected uint EventsCookie + { + get + { + return this.eventsCookie; + } + } + + protected IVsMonitorSelection SelectionMonitor + { + get + { + return this.monSel; + } + } + + protected ServiceProvider ServiceProvider + { + get + { + return this.serviceProvider; + } + } + #endregion + + #region IVsSelectionEvents Members + + public virtual int OnCmdUIContextChanged(uint dwCmdUICookie, int fActive) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnElementValueChanged(uint elementid, object varValueOld, object varValueNew) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnSelectionChanged(IVsHierarchy pHierOld, uint itemidOld, IVsMultiItemSelect pMISOld, ISelectionContainer pSCOld, IVsHierarchy pHierNew, uint itemidNew, IVsMultiItemSelect pMISNew, ISelectionContainer pSCNew) + { + return VSConstants.E_NOTIMPL; + } + + #endregion + + #region IDisposable Members + /// + /// The IDispose interface Dispose method for disposing the object determinastically. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + #endregion + + #region methods + public void Init() + { + if(this.SelectionMonitor != null) + { + ErrorHandler.ThrowOnFailure(this.SelectionMonitor.AdviseSelectionEvents(this, out this.eventsCookie)); + } + } + + /// + /// The method that does the cleanup. + /// + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "Microsoft.VisualStudio.Shell.Interop.IVsMonitorSelection.UnadviseSelectionEvents(System.UInt32)")] + protected virtual void Dispose(bool disposing) + { + // Everybody can go here. + if(!this.isDisposed) + { + // Synchronize calls to the Dispose simulteniously. + lock(Mutex) + { + if(disposing && this.eventsCookie != (uint)ShellConstants.VSCOOKIE_NIL && this.SelectionMonitor != null) + { + this.SelectionMonitor.UnadviseSelectionEvents((uint)this.eventsCookie); + this.eventsCookie = (uint)ShellConstants.VSCOOKIE_NIL; + } + + this.isDisposed = true; + } + } + } + #endregion + + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/SettingsPage.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/SettingsPage.cs new file mode 100644 index 0000000000..3553098fe4 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/SettingsPage.cs @@ -0,0 +1,490 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.ComponentModel; +using System.Drawing; +using System.Globalization; +using System.Runtime.InteropServices; +using System.Security.Permissions; +using System.Windows.Forms; +using Microsoft.VisualStudio.Designer.Interfaces; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.VisualStudio.Project +{ + + /// + /// The base class for property pages. + /// + [CLSCompliant(false), ComVisible(true)] + public abstract class SettingsPage : + LocalizableProperties, + IPropertyPage, + IDisposable + { + #region fields + private Panel panel; + private bool active; + private bool dirty; + private IPropertyPageSite site; + private ProjectNode project; + private ProjectConfig[] projectConfigs; + private IVSMDPropertyGrid grid; + private string name; + private static volatile object Mutex = new object(); + private bool isDisposed; + #endregion + + #region properties + + [Browsable(false)] + [AutomationBrowsable(false)] + public string Name + { + get + { + return this.name; + } + set + { + this.name = value; + } + } + + [Browsable(false)] + [AutomationBrowsable(false)] + public ProjectNode ProjectMgr + { + get + { + return this.project; + } + } + + protected IVSMDPropertyGrid Grid + { + get { return this.grid; } + } + + protected bool IsDirty + { + get + { + return this.dirty; + } + set + { + if(this.dirty != value) + { + this.dirty = value; + if(this.site != null) + site.OnStatusChange((uint)(this.dirty ? PropPageStatus.Dirty : PropPageStatus.Clean)); + } + } + } + protected Panel ThePanel + { + get + { + return this.panel; + } + } + #endregion + + #region abstract methods + protected abstract void BindProperties(); + protected abstract int ApplyChanges(); + #endregion + + #region public methods + public object GetTypedConfigProperty(string name, Type type) + { + string value = GetConfigProperty(name); + if(string.IsNullOrEmpty(value)) return null; + + TypeConverter tc = TypeDescriptor.GetConverter(type); + return tc.ConvertFromInvariantString(value); + } + + public object GetTypedProperty(string name, Type type) + { + string value = GetProperty(name); + if(string.IsNullOrEmpty(value)) return null; + + TypeConverter tc = TypeDescriptor.GetConverter(type); + return tc.ConvertFromInvariantString(value); + } + + public string GetProperty(string propertyName) + { + if(this.ProjectMgr != null) + { + string property; + bool found = this.ProjectMgr.BuildProject.GlobalProperties.TryGetValue(propertyName, out property); + + if(found) + { + return property; + } + } + + return String.Empty; + } + + // relative to active configuration. + public string GetConfigProperty(string propertyName) + { + if(this.ProjectMgr != null) + { + string unifiedResult = null; + bool cacheNeedReset = true; + + for(int i = 0; i < this.projectConfigs.Length; i++) + { + ProjectConfig config = projectConfigs[i]; + string property = config.GetConfigurationProperty(propertyName, cacheNeedReset); + cacheNeedReset = false; + + if(property != null) + { + string text = property.Trim(); + + if(i == 0) + unifiedResult = text; + else if(unifiedResult != text) + return ""; // tristate value is blank then + } + } + + return unifiedResult; + } + + return String.Empty; + } + + /// + /// Sets the value of a configuration dependent property. + /// If the attribute does not exist it is created. + /// If value is null it will be set to an empty string. + /// + /// property name. + /// value of property + public void SetConfigProperty(string name, string value) + { + CCITracing.TraceCall(); + if(value == null) + { + value = String.Empty; + } + + if(this.ProjectMgr != null) + { + for(int i = 0, n = this.projectConfigs.Length; i < n; i++) + { + ProjectConfig config = projectConfigs[i]; + + config.SetConfigurationProperty(name, value); + } + + this.ProjectMgr.SetProjectFileDirty(true); + } + } + + #endregion + + #region IPropertyPage methods. + public virtual void Activate(IntPtr parent, RECT[] pRect, int bModal) + { + if(this.panel == null) + { + if (pRect == null) + { + throw new ArgumentNullException("pRect"); + } + + this.panel = new Panel(); + this.panel.Size = new Size(pRect[0].right - pRect[0].left, pRect[0].bottom - pRect[0].top); + this.panel.Text = SR.GetString(SR.Settings, CultureInfo.CurrentUICulture); + this.panel.Visible = false; + this.panel.Size = new Size(550, 300); + this.panel.CreateControl(); + NativeMethods.SetParent(this.panel.Handle, parent); + } + + if(this.grid == null && this.project != null && this.project.Site != null) + { + IVSMDPropertyBrowser pb = this.project.Site.GetService(typeof(IVSMDPropertyBrowser)) as IVSMDPropertyBrowser; + this.grid = pb.CreatePropertyGrid(); + } + + if(this.grid != null) + { + this.active = true; + + + Control cGrid = Control.FromHandle(new IntPtr(this.grid.Handle)); + + cGrid.Parent = Control.FromHandle(parent);//this.panel; + cGrid.Size = new Size(544, 294); + cGrid.Location = new Point(3, 3); + cGrid.Visible = true; + this.grid.SetOption(_PROPERTYGRIDOPTION.PGOPT_TOOLBAR, false); + this.grid.GridSort = _PROPERTYGRIDSORT.PGSORT_CATEGORIZED | _PROPERTYGRIDSORT.PGSORT_ALPHABETICAL; + NativeMethods.SetParent(new IntPtr(this.grid.Handle), this.panel.Handle); + UpdateObjects(); + } + } + + public virtual int Apply() + { + if(IsDirty) + { + return this.ApplyChanges(); + } + return VSConstants.S_OK; + } + + public virtual void Deactivate() + { + if(null != this.panel) + { + this.panel.Dispose(); + this.panel = null; + } + this.active = false; + } + + public virtual void GetPageInfo(PROPPAGEINFO[] arrInfo) + { + if (arrInfo == null) + { + throw new ArgumentNullException("arrInfo"); + } + + PROPPAGEINFO info = new PROPPAGEINFO(); + + info.cb = (uint)Marshal.SizeOf(typeof(PROPPAGEINFO)); + info.dwHelpContext = 0; + info.pszDocString = null; + info.pszHelpFile = null; + info.pszTitle = this.name; + info.SIZE.cx = 550; + info.SIZE.cy = 300; + arrInfo[0] = info; + } + + public virtual void Help(string helpDir) + { + } + + public virtual int IsPageDirty() + { + // Note this returns an HRESULT not a Bool. + return (IsDirty ? (int)VSConstants.S_OK : (int)VSConstants.S_FALSE); + } + + public virtual void Move(RECT[] arrRect) + { + if (arrRect == null) + { + throw new ArgumentNullException("arrRect"); + } + + RECT r = arrRect[0]; + + this.panel.Location = new Point(r.left, r.top); + this.panel.Size = new Size(r.right - r.left, r.bottom - r.top); + } + + public virtual void SetObjects(uint count, object[] punk) + { + if (punk == null) + { + return; + } + + if(count > 0) + { + if(punk[0] is ProjectConfig) + { + ArrayList configs = new ArrayList(); + + for(int i = 0; i < count; i++) + { + ProjectConfig config = (ProjectConfig)punk[i]; + + if(this.project == null) + { + this.project = config.ProjectMgr; + } + + configs.Add(config); + } + + this.projectConfigs = (ProjectConfig[])configs.ToArray(typeof(ProjectConfig)); + } + else if(punk[0] is NodeProperties) + { + if(this.project == null) + { + this.project = (punk[0] as NodeProperties).Node.ProjectMgr; + } + + System.Collections.Generic.Dictionary configsMap = new System.Collections.Generic.Dictionary(); + + for(int i = 0; i < count; i++) + { + NodeProperties property = (NodeProperties)punk[i]; + IVsCfgProvider provider; + ErrorHandler.ThrowOnFailure(property.Node.ProjectMgr.GetCfgProvider(out provider)); + uint[] expected = new uint[1]; + ErrorHandler.ThrowOnFailure(provider.GetCfgs(0, null, expected, null)); + if(expected[0] > 0) + { + ProjectConfig[] configs = new ProjectConfig[expected[0]]; + uint[] actual = new uint[1]; + ErrorHandler.ThrowOnFailure(provider.GetCfgs(expected[0], configs, actual, null)); + + foreach(ProjectConfig config in configs) + { + if(!configsMap.ContainsKey(config.ConfigName)) + { + configsMap.Add(config.ConfigName, config); + } + } + } + } + + if(configsMap.Count > 0) + { + if(this.projectConfigs == null) + { + this.projectConfigs = new ProjectConfig[configsMap.Keys.Count]; + } + configsMap.Values.CopyTo(this.projectConfigs, 0); + } + } + } + else + { + this.project = null; + } + + if(this.active && this.project != null) + { + UpdateObjects(); + } + } + + + public virtual void SetPageSite(IPropertyPageSite theSite) + { + this.site = theSite; + } + + public virtual void Show(uint cmd) + { + this.panel.Visible = true; // TODO: pass SW_SHOW* flags through + this.panel.Show(); + } + + public virtual int TranslateAccelerator(MSG[] arrMsg) + { + if (arrMsg == null) + { + throw new ArgumentNullException("arrMsg"); + } + + MSG msg = arrMsg[0]; + + if((msg.message < NativeMethods.WM_KEYFIRST || msg.message > NativeMethods.WM_KEYLAST) && (msg.message < NativeMethods.WM_MOUSEFIRST || msg.message > NativeMethods.WM_MOUSELAST)) + return 1; + + return (NativeMethods.IsDialogMessageA(this.panel.Handle, ref msg)) ? 0 : 1; + } + + #endregion + + #region helper methods + + protected ProjectConfig[] GetProjectConfigurations() + { + return this.projectConfigs; + } + + protected void UpdateObjects() + { + if(this.projectConfigs != null && this.project != null) + { + // Demand unmanaged permissions in order to access unmanaged memory. + new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); + + IntPtr p = Marshal.GetIUnknownForObject(this); + IntPtr ppUnk = Marshal.AllocCoTaskMem(Marshal.SizeOf(typeof(IntPtr))); + try + { + Marshal.WriteIntPtr(ppUnk, p); + this.BindProperties(); + // BUGBUG -- this is really bad casting a pointer to "int"... + this.grid.SetSelectedObjects(1, ppUnk.ToInt32()); + this.grid.Refresh(); + } + finally + { + if(ppUnk != IntPtr.Zero) + { + Marshal.FreeCoTaskMem(ppUnk); + } + if(p != IntPtr.Zero) + { + Marshal.Release(p); + } + } + } + } + #endregion + + #region IDisposable Members + + /// + /// Performs application-defined tasks associated with freeing, releasing, or resetting unmanaged resources. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + + private void Dispose(bool disposing) + { + if(!this.isDisposed) + { + lock(Mutex) + { + if(disposing) + { + this.panel.Dispose(); + } + + this.isDisposed = true; + } + } + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/SingleFileGenerator.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/SingleFileGenerator.cs new file mode 100644 index 0000000000..629fbc13a5 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/SingleFileGenerator.cs @@ -0,0 +1,520 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.VisualStudio.TextManager.Interop; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Provides support for single file generator. + /// + internal class SingleFileGenerator : ISingleFileGenerator, IVsGeneratorProgress + { + + #region fields + private bool gettingCheckoutStatus; + private bool runningGenerator; + private ProjectNode projectMgr; + #endregion + + #region ctors + /// + /// Overloadde ctor. + /// + /// The associated project + internal SingleFileGenerator(ProjectNode projectMgr) + { + this.projectMgr = projectMgr; + } + #endregion + + #region IVsGeneratorProgress Members + + public virtual int GeneratorError(int warning, uint level, string err, uint line, uint col) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int Progress(uint complete, uint total) + { + return VSConstants.E_NOTIMPL; + } + + #endregion + + #region ISingleFileGenerator + /// + /// Runs the generator on the current project item. + /// + /// + /// + public virtual void RunGenerator(string document) + { + // Go run the generator on that node, but only if the file is dirty + // in the running document table. Otherwise there is no need to rerun + // the generator because if the original document is not dirty then + // the generated output should be already up to date. + uint itemid = VSConstants.VSITEMID_NIL; + IVsHierarchy hier = (IVsHierarchy)this.projectMgr; + if(document != null && hier != null && ErrorHandler.Succeeded(hier.ParseCanonicalName((string)document, out itemid))) + { + IVsHierarchy rdtHier; + IVsPersistDocData perDocData; + uint cookie; + if(this.VerifyFileDirtyInRdt((string)document, out rdtHier, out perDocData, out cookie)) + { + // Run the generator on the indicated document + FileNode node = (FileNode)this.projectMgr.NodeFromItemId(itemid); + this.InvokeGenerator(node); + } + } + } + #endregion + + #region virtual methods + /// + /// Invokes the specified generator + /// + /// The node on which to invoke the generator. + protected internal virtual void InvokeGenerator(FileNode fileNode) + { + if(fileNode == null) + { + throw new ArgumentNullException("fileNode"); + } + + SingleFileGeneratorNodeProperties nodeproperties = fileNode.NodeProperties as SingleFileGeneratorNodeProperties; + if(nodeproperties == null) + { + throw new InvalidOperationException(); + } + + string customToolProgID = nodeproperties.CustomTool; + if(string.IsNullOrEmpty(customToolProgID)) + { + return; + } + + string customToolNamespace = nodeproperties.CustomToolNamespace; + + try + { + if(!this.runningGenerator) + { + //Get the buffer contents for the current node + string moniker = fileNode.GetMkDocument(); + + this.runningGenerator = true; + + //Get the generator + IVsSingleFileGenerator generator; + int generateDesignTimeSource; + int generateSharedDesignTimeSource; + int generateTempPE; + SingleFileGeneratorFactory factory = new SingleFileGeneratorFactory(this.projectMgr.ProjectGuid, this.projectMgr.Site); + ErrorHandler.ThrowOnFailure(factory.CreateGeneratorInstance(customToolProgID, out generateDesignTimeSource, out generateSharedDesignTimeSource, out generateTempPE, out generator)); + + //Check to see if the generator supports siting + IObjectWithSite objWithSite = generator as IObjectWithSite; + if(objWithSite != null) + { + objWithSite.SetSite(fileNode.OleServiceProvider); + } + + //Determine the namespace + if(string.IsNullOrEmpty(customToolNamespace)) + { + customToolNamespace = this.ComputeNamespace(moniker); + } + + //Run the generator + IntPtr[] output = new IntPtr[1]; + output[0] = IntPtr.Zero; + uint outPutSize; + string extension; + ErrorHandler.ThrowOnFailure(generator.DefaultExtension(out extension)); + + //Find if any dependent node exists + string dependentNodeName = Path.GetFileNameWithoutExtension(fileNode.FileName) + extension; + HierarchyNode dependentNode = fileNode.FirstChild; + while(dependentNode != null) + { + if(string.Compare(dependentNode.ItemNode.GetMetadata(ProjectFileConstants.DependentUpon), fileNode.FileName, StringComparison.OrdinalIgnoreCase) == 0) + { + dependentNodeName = ((FileNode)dependentNode).FileName; + break; + } + + dependentNode = dependentNode.NextSibling; + } + + //If you found a dependent node. + if(dependentNode != null) + { + //Then check out the node and dependent node from SCC + if(!this.CanEditFile(dependentNode.GetMkDocument())) + { + throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + } + else //It is a new node to be added to the project + { + // Check out the project file if necessary. + if(!this.projectMgr.QueryEditProjectFile(false)) + { + throw Marshal.GetExceptionForHR(VSConstants.OLE_E_PROMPTSAVECANCELLED); + } + } + IVsTextStream stream; + string inputFileContents = this.GetBufferContents(moniker, out stream); + + ErrorHandler.ThrowOnFailure(generator.Generate(moniker, inputFileContents, customToolNamespace, output, out outPutSize, this)); + byte[] data = new byte[outPutSize]; + + if(output[0] != IntPtr.Zero) + { + Marshal.Copy(output[0], data, 0, (int)outPutSize); + Marshal.FreeCoTaskMem(output[0]); + } + + //Todo - Create a file and add it to the Project + this.UpdateGeneratedCodeFile(fileNode, data, (int)outPutSize, dependentNodeName); + } + } + finally + { + this.runningGenerator = false; + } + } + + /// + /// Computes the names space based on the folder for the ProjectItem. It just replaces DirectorySeparatorCharacter + /// with "." for the directory in which the file is located. + /// + /// Returns the computed name space + protected virtual string ComputeNamespace(string projectItemPath) + { + if(String.IsNullOrEmpty(projectItemPath)) + { + throw new ArgumentException(SR.GetString(SR.ParameterCannotBeNullOrEmpty, CultureInfo.CurrentUICulture), "projectItemPath"); + } + + + string nspace = ""; + string filePath = Path.GetDirectoryName(projectItemPath); + string[] toks = filePath.Split(new char[] { ':', '\\' }); + foreach(string tok in toks) + { + if(!String.IsNullOrEmpty(tok)) + { + string temp = tok.Replace(" ", ""); + nspace += (temp + "."); + } + } + nspace = nspace.Remove(nspace.LastIndexOf(".", StringComparison.Ordinal), 1); + return nspace; + } + + /// + /// This is called after the single file generator has been invoked to create or update the code file. + /// + /// The node associated to the generator + /// data to update the file with + /// size of the data + /// Name of the file to update or create + /// full path of the file + protected virtual string UpdateGeneratedCodeFile(FileNode fileNode, byte[] data, int size, string fileName) + { + string filePath = Path.Combine(Path.GetDirectoryName(fileNode.GetMkDocument()), fileName); + IVsRunningDocumentTable rdt = this.projectMgr.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + + // (kberes) Shouldn't this be an InvalidOperationException instead with some not to annoying errormessage to the user? + if(rdt == null) + { + ErrorHandler.ThrowOnFailure(VSConstants.E_FAIL); + } + + IVsHierarchy hier; + uint cookie; + uint itemid; + IntPtr docData = IntPtr.Zero; + ErrorHandler.ThrowOnFailure(rdt.FindAndLockDocument((uint)(_VSRDTFLAGS.RDT_NoLock), filePath, out hier, out itemid, out docData, out cookie)); + if(docData != IntPtr.Zero) + { + Marshal.Release(docData); + IVsTextStream srpStream = null; + if(srpStream != null) + { + int oldLen = 0; + int hr = srpStream.GetSize(out oldLen); + if(ErrorHandler.Succeeded(hr)) + { + IntPtr dest = IntPtr.Zero; + try + { + dest = Marshal.AllocCoTaskMem(data.Length); + Marshal.Copy(data, 0, dest, data.Length); + ErrorHandler.ThrowOnFailure(srpStream.ReplaceStream(0, oldLen, dest, size / 2)); + } + finally + { + if(dest != IntPtr.Zero) + { + Marshal.Release(dest); + } + } + } + } + } + else + { + using(FileStream generatedFileStream = File.Open(filePath, FileMode.OpenOrCreate)) + { + generatedFileStream.Write(data, 0, size); + } + + EnvDTE.ProjectItem projectItem = fileNode.GetAutomationObject() as EnvDTE.ProjectItem; + if(projectItem != null && (this.projectMgr.FindChild(fileNode.FileName) == null)) + { + projectItem.ProjectItems.AddFromFile(filePath); + } + } + return filePath; + } + #endregion + + #region helpers + /// + /// Returns the buffer contents for a moniker. + /// + /// Buffer contents + private string GetBufferContents(string fileName, out IVsTextStream srpStream) + { + Guid CLSID_VsTextBuffer = new Guid("{8E7B96A8-E33D-11d0-A6D5-00C04FB67F6A}"); + string bufferContents = ""; + srpStream = null; + + IVsRunningDocumentTable rdt = this.projectMgr.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + if(rdt != null) + { + IVsHierarchy hier; + IVsPersistDocData persistDocData; + uint itemid, cookie; + bool docInRdt = true; + IntPtr docData = IntPtr.Zero; + int hr = NativeMethods.E_FAIL; + try + { + //Getting a read lock on the document. Must be released later. + hr = rdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_ReadLock, fileName, out hier, out itemid, out docData, out cookie); + if(ErrorHandler.Failed(hr) || docData == IntPtr.Zero) + { + Guid iid = VSConstants.IID_IUnknown; + cookie = 0; + docInRdt = false; + ILocalRegistry localReg = this.projectMgr.GetService(typeof(SLocalRegistry)) as ILocalRegistry; + ErrorHandler.ThrowOnFailure(localReg.CreateInstance(CLSID_VsTextBuffer, null, ref iid, (uint)CLSCTX.CLSCTX_INPROC_SERVER, out docData)); + } + + persistDocData = Marshal.GetObjectForIUnknown(docData) as IVsPersistDocData; + } + finally + { + if(docData != IntPtr.Zero) + { + Marshal.Release(docData); + } + } + + //Try to get the Text lines + IVsTextLines srpTextLines = persistDocData as IVsTextLines; + if(srpTextLines == null) + { + // Try getting a text buffer provider first + IVsTextBufferProvider srpTextBufferProvider = persistDocData as IVsTextBufferProvider; + if(srpTextBufferProvider != null) + { + hr = srpTextBufferProvider.GetTextBuffer(out srpTextLines); + } + } + + if(ErrorHandler.Succeeded(hr)) + { + srpStream = srpTextLines as IVsTextStream; + if(srpStream != null) + { + // QI for IVsBatchUpdate and call FlushPendingUpdates if they support it + IVsBatchUpdate srpBatchUpdate = srpStream as IVsBatchUpdate; + if(srpBatchUpdate != null) + ErrorHandler.ThrowOnFailure(srpBatchUpdate.FlushPendingUpdates(0)); + + int lBufferSize = 0; + hr = srpStream.GetSize(out lBufferSize); + + if(ErrorHandler.Succeeded(hr)) + { + IntPtr dest = IntPtr.Zero; + try + { + // Note that GetStream returns Unicode to us so we don't need to do any conversions + dest = Marshal.AllocCoTaskMem((lBufferSize + 1) * 2); + ErrorHandler.ThrowOnFailure(srpStream.GetStream(0, lBufferSize, dest)); + //Get the contents + bufferContents = Marshal.PtrToStringUni(dest); + } + finally + { + if(dest != IntPtr.Zero) + Marshal.FreeCoTaskMem(dest); + } + } + } + + } + // Unlock the document in the RDT if necessary + if(docInRdt && rdt != null) + { + ErrorHandler.ThrowOnFailure(rdt.UnlockDocument((uint)(_VSRDTFLAGS.RDT_ReadLock | _VSRDTFLAGS.RDT_Unlock_NoSave), cookie)); + } + + if(ErrorHandler.Failed(hr)) + { + // If this failed then it's probably not a text file. In that case, + // we just read the file as a binary + bufferContents = File.ReadAllText(fileName); + } + + + } + return bufferContents; + } + + /// + /// Returns TRUE if open and dirty. Note that documents can be open without a + /// window frame so be careful. Returns the DocData and doc cookie if requested + /// + /// document path + /// hierarchy + /// doc data associated with document + /// item cookie + /// True if FIle is dirty + private bool VerifyFileDirtyInRdt(string document, out IVsHierarchy pHier, out IVsPersistDocData ppDocData, out uint cookie) + { + int ret = 0; + pHier = null; + ppDocData = null; + cookie = 0; + + IVsRunningDocumentTable rdt = this.projectMgr.GetService(typeof(IVsRunningDocumentTable)) as IVsRunningDocumentTable; + if(rdt != null) + { + IntPtr docData; + uint dwCookie = 0; + IVsHierarchy srpHier; + uint itemid = VSConstants.VSITEMID_NIL; + + ErrorHandler.ThrowOnFailure(rdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, document, out srpHier, out itemid, out docData, out dwCookie)); + IVsPersistHierarchyItem srpIVsPersistHierarchyItem = srpHier as IVsPersistHierarchyItem; + if(srpIVsPersistHierarchyItem != null) + { + // Found in the RDT. See if it is dirty + try + { + ErrorHandler.ThrowOnFailure(srpIVsPersistHierarchyItem.IsItemDirty(itemid, docData, out ret)); + cookie = dwCookie; + ppDocData = Marshal.GetObjectForIUnknown(docData) as IVsPersistDocData; + } + finally + { + if(docData != IntPtr.Zero) + { + Marshal.Release(docData); + } + + pHier = srpHier; + } + } + } + return (ret == 1); + } + #endregion + + + + + #region QueryEditQuerySave helpers + /// + /// This function asks to the QueryEditQuerySave service if it is possible to + /// edit the file. + /// + private bool CanEditFile(string documentMoniker) + { + Trace.WriteLine(string.Format(CultureInfo.CurrentCulture, "\t**** CanEditFile called ****")); + + // Check the status of the recursion guard + if(this.gettingCheckoutStatus) + { + return false; + } + + try + { + // Set the recursion guard + this.gettingCheckoutStatus = true; + + // Get the QueryEditQuerySave service + IVsQueryEditQuerySave2 queryEditQuerySave = (IVsQueryEditQuerySave2)this.projectMgr.GetService(typeof(SVsQueryEditQuerySave)); + + // Now call the QueryEdit method to find the edit status of this file + string[] documents = { documentMoniker }; + uint result; + uint outFlags; + + // Note that this function can popup a dialog to ask the user to checkout the file. + // When this dialog is visible, it is possible to receive other request to change + // the file and this is the reason for the recursion guard. + int hr = queryEditQuerySave.QueryEditFiles( + 0, // Flags + 1, // Number of elements in the array + documents, // Files to edit + null, // Input flags + null, // Input array of VSQEQS_FILE_ATTRIBUTE_DATA + out result, // result of the checkout + out outFlags // Additional flags + ); + + if(ErrorHandler.Succeeded(hr) && (result == (uint)tagVSQueryEditResult.QER_EditOK)) + { + // In this case (and only in this case) we can return true from this function. + return true; + } + } + finally + { + this.gettingCheckoutStatus = false; + } + + return false; + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/SingleFileGeneratorFactory.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/SingleFileGeneratorFactory.cs new file mode 100644 index 0000000000..d05392f672 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/SingleFileGeneratorFactory.cs @@ -0,0 +1,364 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.Win32; +using VSRegistry = Microsoft.VisualStudio.Shell.VSRegistry; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Provides implementation IVsSingleFileGeneratorFactory for + /// + public class SingleFileGeneratorFactory : IVsSingleFileGeneratorFactory + { + #region nested types + private class GeneratorMetaData + { + #region fields + private Guid generatorClsid = Guid.Empty; + private int generatesDesignTimeSource = -1; + private int generatesSharedDesignTimeSource = -1; + private int useDesignTimeCompilationFlag = -1; + object generator; + #endregion + + #region ctor + /// + /// Constructor + /// + public GeneratorMetaData() + { + } + #endregion + + #region Public Properties + /// + /// Generator instance + /// + public Object Generator + { + get + { + return generator; + } + set + { + generator = value; + } + } + + /// + /// GeneratesDesignTimeSource reg value name under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\[VsVer]\Generators\[ProjFacGuid]\[GeneratorProgId] + /// + public int GeneratesDesignTimeSource + { + get + { + return generatesDesignTimeSource; + } + set + { + generatesDesignTimeSource = value; + } + } + + /// + /// GeneratesSharedDesignTimeSource reg value name under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\[VsVer]\Generators\[ProjFacGuid]\[GeneratorProgId] + /// + public int GeneratesSharedDesignTimeSource + { + get + { + return generatesSharedDesignTimeSource; + } + set + { + generatesSharedDesignTimeSource = value; + } + } + + /// + /// UseDesignTimeCompilationFlag reg value name under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\[VsVer]\Generators\[ProjFacGuid]\[GeneratorProgId] + /// + public int UseDesignTimeCompilationFlag + { + get + { + return useDesignTimeCompilationFlag; + } + set + { + useDesignTimeCompilationFlag = value; + } + } + + /// + /// Generator Class ID. + /// + public Guid GeneratorClsid + { + get + { + return generatorClsid; + } + set + { + generatorClsid = value; + } + } + #endregion + } + #endregion + + #region fields + /// + /// Base generator registry key for MPF based project + /// + private RegistryKey baseGeneratorRegistryKey; + + /// + /// CLSID reg value name under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\[VsVer]\Generators\[ProjFacGuid]\[GeneratorProgId] + /// + private string GeneratorClsid = "CLSID"; + + /// + /// GeneratesDesignTimeSource reg value name under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\[VsVer]\Generators\[ProjFacGuid]\[GeneratorProgId] + /// + private string GeneratesDesignTimeSource = "GeneratesDesignTimeSource"; + + /// + /// GeneratesSharedDesignTimeSource reg value name under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\[VsVer]\Generators\[ProjFacGuid]\[GeneratorProgId] + /// + private string GeneratesSharedDesignTimeSource = "GeneratesSharedDesignTimeSource"; + + /// + /// UseDesignTimeCompilationFlag reg value name under HKEY_LOCAL_MACHINE\SOFTWARE\Microsoft\VisualStudio\[VsVer]\Generators\[ProjFacGuid]\[GeneratorProgId] + /// + private string UseDesignTimeCompilationFlag = "UseDesignTimeCompilationFlag"; + + /// + /// Caches all the generators registered for the project type. + /// + private Dictionary generatorsMap = new Dictionary(); + + /// + /// The project type guid of the associated project. + /// + private Guid projectType; + + /// + /// A service provider + /// + private System.IServiceProvider serviceProvider; + #endregion + + #region ctors + /// + /// Constructor for SingleFileGeneratorFactory + /// + /// The project type guid of the associated project. + /// A service provider. + public SingleFileGeneratorFactory(Guid projectType, System.IServiceProvider serviceProvider) + { + this.projectType = projectType; + this.serviceProvider = serviceProvider; + } + #endregion + + #region properties + /// + /// Defines the project type guid of the associated project. + /// + public Guid ProjectGuid + { + get { return this.projectType; } + set { this.projectType = value; } + } + + /// + /// Defines an associated service provider. + /// + public System.IServiceProvider ServiceProvider + { + get { return this.serviceProvider; } + set { this.serviceProvider = value; } + } + #endregion + + #region IVsSingleFileGeneratorFactory Helpers + /// + /// Returns the project generator key under [VS-ConfigurationRoot]]\Generators + /// + private RegistryKey BaseGeneratorsKey + { + get + { + if(this.baseGeneratorRegistryKey == null) + { + using(RegistryKey root = VSRegistry.RegistryRoot(__VsLocalRegistryType.RegType_Configuration)) + { + if(null != root) + { + string regPath = "Generators\\" + this.ProjectGuid.ToString("B"); + baseGeneratorRegistryKey = root.OpenSubKey(regPath); + } + } + } + + return this.baseGeneratorRegistryKey; + } + } + + /// + /// Returns the local registry instance + /// + private ILocalRegistry LocalRegistry + { + get + { + return this.serviceProvider.GetService(typeof(SLocalRegistry)) as ILocalRegistry; + } + } + #endregion + + #region IVsSingleFileGeneratorFactory Members + /// + /// Creates an instance of the single file generator requested + /// + /// prog id of the generator to be created. For e.g HKLM\SOFTWARE\Microsoft\VisualStudio\9.0Exp\Generators\[prjfacguid]\[wszProgId] + /// GeneratesDesignTimeSource key value + /// GeneratesSharedDesignTimeSource key value + /// UseDesignTimeCompilationFlag key value + /// IVsSingleFileGenerator interface + /// S_OK if succesful + public virtual int CreateGeneratorInstance(string progId, out int generatesDesignTimeSource, out int generatesSharedDesignTimeSource, out int useTempPEFlag, out IVsSingleFileGenerator generate) + { + Guid genGuid; + ErrorHandler.ThrowOnFailure(this.GetGeneratorInformation(progId, out generatesDesignTimeSource, out generatesSharedDesignTimeSource, out useTempPEFlag, out genGuid)); + + //Create the single file generator and pass it out. Check to see if it is in the cache + if(!this.generatorsMap.ContainsKey(progId) || ((this.generatorsMap[progId]).Generator == null)) + { + Guid riid = VSConstants.IID_IUnknown; + uint dwClsCtx = (uint)CLSCTX.CLSCTX_INPROC_SERVER; + IntPtr genIUnknown = IntPtr.Zero; + //create a new one. + ErrorHandler.ThrowOnFailure(this.LocalRegistry.CreateInstance(genGuid, null, ref riid, dwClsCtx, out genIUnknown)); + if(genIUnknown != IntPtr.Zero) + { + try + { + object generator = Marshal.GetObjectForIUnknown(genIUnknown); + //Build the generator meta data object and cache it. + GeneratorMetaData genData = new GeneratorMetaData(); + genData.GeneratesDesignTimeSource = generatesDesignTimeSource; + genData.GeneratesSharedDesignTimeSource = generatesSharedDesignTimeSource; + genData.UseDesignTimeCompilationFlag = useTempPEFlag; + genData.GeneratorClsid = genGuid; + genData.Generator = generator; + this.generatorsMap[progId] = genData; + } + finally + { + Marshal.Release(genIUnknown); + } + } + } + + generate = (this.generatorsMap[progId]).Generator as IVsSingleFileGenerator; + + return VSConstants.S_OK; + } + + /// + /// Gets the default generator based on the file extension. HKLM\Software\Microsoft\VS\9.0\Generators\[prjfacguid]\.extension + /// + /// File name with extension + /// The generator prog ID + /// S_OK if successful + public virtual int GetDefaultGenerator(string filename, out string progID) + { + progID = ""; + return VSConstants.E_NOTIMPL; + } + + /// + /// Gets the generator information. + /// + /// prog id of the generator to be created. For e.g HKLM\SOFTWARE\Microsoft\VisualStudio\9.0Exp\Generators\[prjfacguid]\[wszProgId] + /// GeneratesDesignTimeSource key value + /// GeneratesSharedDesignTimeSource key value + /// UseDesignTimeCompilationFlag key value + /// CLSID key value + /// S_OK if succesful + public virtual int GetGeneratorInformation(string progId, out int generatesDesignTimeSource, out int generatesSharedDesignTimeSource, out int useTempPEFlag, out Guid guidGenerator) + { + RegistryKey genKey; + generatesDesignTimeSource = -1; + generatesSharedDesignTimeSource = -1; + useTempPEFlag = -1; + guidGenerator = Guid.Empty; + if(string.IsNullOrEmpty(progId)) + return VSConstants.S_FALSE; + + //Create the single file generator and pass it out. + if(!this.generatorsMap.ContainsKey(progId)) + { + // We have to check whether the BaseGeneratorkey returns null. + RegistryKey tempBaseGeneratorKey = this.BaseGeneratorsKey; + if(tempBaseGeneratorKey == null || (genKey = tempBaseGeneratorKey.OpenSubKey(progId)) == null) + { + return VSConstants.S_FALSE; + } + + //Get the CLSID + string guid = (string)genKey.GetValue(GeneratorClsid, ""); + if(string.IsNullOrEmpty(guid)) + return VSConstants.S_FALSE; + + GeneratorMetaData genData = new GeneratorMetaData(); + + genData.GeneratorClsid = guidGenerator = new Guid(guid); + //Get the GeneratesDesignTimeSource flag. Assume 0 if not present. + genData.GeneratesDesignTimeSource = generatesDesignTimeSource = (int)genKey.GetValue(this.GeneratesDesignTimeSource, 0); + //Get the GeneratesSharedDesignTimeSource flag. Assume 0 if not present. + genData.GeneratesSharedDesignTimeSource = generatesSharedDesignTimeSource = (int)genKey.GetValue(GeneratesSharedDesignTimeSource, 0); + //Get the UseDesignTimeCompilationFlag flag. Assume 0 if not present. + genData.UseDesignTimeCompilationFlag = useTempPEFlag = (int)genKey.GetValue(UseDesignTimeCompilationFlag, 0); + this.generatorsMap.Add(progId, genData); + } + else + { + GeneratorMetaData genData = this.generatorsMap[progId]; + generatesDesignTimeSource = genData.GeneratesDesignTimeSource; + //Get the GeneratesSharedDesignTimeSource flag. Assume 0 if not present. + generatesSharedDesignTimeSource = genData.GeneratesSharedDesignTimeSource; + //Get the UseDesignTimeCompilationFlag flag. Assume 0 if not present. + useTempPEFlag = genData.UseDesignTimeCompilationFlag; + //Get the CLSID + guidGenerator = genData.GeneratorClsid; + } + + return VSConstants.S_OK; + } + #endregion + + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/SolutionListener.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/SolutionListener.cs new file mode 100644 index 0000000000..990c6a7e26 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/SolutionListener.cs @@ -0,0 +1,235 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using IServiceProvider = System.IServiceProvider; +using ShellConstants = Microsoft.VisualStudio.Shell.Interop.Constants; + +namespace Microsoft.VisualStudio.Project +{ + + [CLSCompliant(false)] + public abstract class SolutionListener : IVsSolutionEvents3, IVsSolutionEvents4, IDisposable + { + + #region fields + private uint eventsCookie; + private IVsSolution solution; + private IServiceProvider serviceProvider; + private bool isDisposed; + /// + /// Defines an object that will be a mutex for this object for synchronizing thread calls. + /// + private static volatile object Mutex = new object(); + #endregion + + #region ctors + protected SolutionListener(IServiceProvider serviceProviderParameter) + { + if (serviceProviderParameter == null) + { + throw new ArgumentNullException("serviceProviderParameter"); + } + + this.serviceProvider = serviceProviderParameter; + this.solution = this.serviceProvider.GetService(typeof(SVsSolution)) as IVsSolution; + + Debug.Assert(this.solution != null, "Could not get the IVsSolution object from the services exposed by this project"); + + if(this.solution == null) + { + throw new InvalidOperationException(); + } + } + #endregion + + #region properties + protected uint EventsCookie + { + get + { + return this.eventsCookie; + } + } + + protected IVsSolution Solution + { + get + { + return this.solution; + } + } + + protected IServiceProvider ServiceProvider + { + get + { + return this.serviceProvider; + } + } + #endregion + + #region IVsSolutionEvents3, IVsSolutionEvents2, IVsSolutionEvents methods + public virtual int OnAfterCloseSolution(object reserved) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnAfterClosingChildren(IVsHierarchy hierarchy) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnAfterLoadProject(IVsHierarchy stubHierarchy, IVsHierarchy realHierarchy) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnAfterMergeSolution(object pUnkReserved) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnAfterOpenProject(IVsHierarchy hierarchy, int added) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnAfterOpenSolution(object pUnkReserved, int fNewSolution) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnAfterOpeningChildren(IVsHierarchy hierarchy) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnBeforeCloseProject(IVsHierarchy hierarchy, int removed) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnBeforeCloseSolution(object pUnkReserved) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnBeforeClosingChildren(IVsHierarchy hierarchy) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnBeforeOpeningChildren(IVsHierarchy hierarchy) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnBeforeUnloadProject(IVsHierarchy realHierarchy, IVsHierarchy rtubHierarchy) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnQueryCloseProject(IVsHierarchy hierarchy, int removing, ref int cancel) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnQueryCloseSolution(object pUnkReserved, ref int cancel) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnQueryUnloadProject(IVsHierarchy pRealHierarchy, ref int cancel) + { + return VSConstants.E_NOTIMPL; + } + #endregion + + #region IVsSolutionEvents4 methods + public virtual int OnAfterAsynchOpenProject(IVsHierarchy hierarchy, int added) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnAfterChangeProjectParent(IVsHierarchy hierarchy) + { + return VSConstants.E_NOTIMPL; + } + + public virtual int OnAfterRenameProject(IVsHierarchy hierarchy) + { + return VSConstants.E_NOTIMPL; + } + + /// + /// Fired before a project is moved from one parent to another in the solution explorer + /// + public virtual int OnQueryChangeProjectParent(IVsHierarchy hierarchy, IVsHierarchy newParentHier, ref int cancel) + { + return VSConstants.E_NOTIMPL; + } + #endregion + + #region Dispose + + /// + /// The IDispose interface Dispose method for disposing the object determinastically. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + + #region methods + public void Init() + { + if(this.solution != null) + { + ErrorHandler.ThrowOnFailure(this.solution.AdviseSolutionEvents(this, out this.eventsCookie)); + } + } + + /// + /// The method that does the cleanup. + /// + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Usage", "CA1806:DoNotIgnoreMethodResults", MessageId = "Microsoft.VisualStudio.Shell.Interop.IVsSolution.UnadviseSolutionEvents(System.UInt32)")] + protected virtual void Dispose(bool disposing) + { + // Everybody can go here. + if(!this.isDisposed) + { + // Synchronize calls to the Dispose simulteniously. + lock(Mutex) + { + if(disposing && this.eventsCookie != (uint)ShellConstants.VSCOOKIE_NIL && this.solution != null) + { + this.solution.UnadviseSolutionEvents((uint)this.eventsCookie); + this.eventsCookie = (uint)ShellConstants.VSCOOKIE_NIL; + } + + this.isDisposed = true; + } + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/SolutionListenerForBuildDependencyUpdate.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/SolutionListenerForBuildDependencyUpdate.cs new file mode 100644 index 0000000000..a71cc725c5 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/SolutionListenerForBuildDependencyUpdate.cs @@ -0,0 +1,179 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using IServiceProvider = System.IServiceProvider; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// The purpose of this class is to set a build dependency from a modeling project to all its sub projects + /// + class SolutionListenerForBuildDependencyUpdate : SolutionListener + { + #region ctors + public SolutionListenerForBuildDependencyUpdate(IServiceProvider serviceProvider) + : base(serviceProvider) + { + } + #endregion + + #region overridden methods + /// + /// Update build dependency list if solution is fully loaded + /// + /// + /// + /// + public override int OnAfterOpenProject(IVsHierarchy hierarchy, int added) + { + // Return from here if we are at load time + if(added == 0) + { + return VSConstants.S_OK; + } + + IBuildDependencyOnProjectContainer projectNode = hierarchy as IBuildDependencyOnProjectContainer; + + // We will update only nested project types and the BuildNestedProjectsOnBuild flag is set to true + if(projectNode != null) + { + if(projectNode.BuildNestedProjectsOnBuild) + { + // Enum all sub projects and add to dependency list + UpdateDependencyListWithSubProjects(projectNode); + } + } + return VSConstants.S_OK; + } + + /// + /// Called at load time when solution has finished opening. + /// + /// reserved + /// true if this is a new solution + /// + public override int OnAfterOpenSolution(object pUnkReserved, int fNewSolution) + { + // Enum all sub project and add to dependeny list + UpdateDependencyListWithSubProjects(null); + + return VSConstants.S_OK; + } + #endregion + + #region Helper methods + /// + /// Update dependency list + /// + /// Project node to be updated. If null then all ProjectContainer nodes are updated + private void UpdateDependencyListWithSubProjects(IBuildDependencyOnProjectContainer projectNode) + { + if(projectNode != null) + { + // Get list of sub projects + IList nestedProjectList = projectNode.EnumNestedHierachiesForBuildDependency(); + if(nestedProjectList != null && nestedProjectList.Count > 0) + { + // Loop nested projects and add project dependency (if supported) + foreach(IVsHierarchy nestedProject in nestedProjectList) + { + AddBuildDependenyToNestedProject(projectNode as IBuildDependencyUpdate, nestedProject); + } + } + } + else + { + // Update all ProjectContainerNode nodes + List projectList = this.GetListOfProjectContainerNodes(); + if(projectList != null && projectList.Count > 0) + { + foreach(IBuildDependencyOnProjectContainer project in projectList) + { + UpdateDependencyListWithSubProjects(project); + } + } + } + } + + /// + /// Enum all projects in the solution and collect all that derives from ProjectContainerNode + /// + /// List of ProjectContainerNode nodes + private List GetListOfProjectContainerNodes() + { + List projectList = new List(); + + Debug.Assert(this.Solution != null, "IVsSolution object not set on this object"); + if(this.Solution == null) + { + // Bad state, so we quit + return projectList; + } + + // Enum projects loaded in the solution (normal projects only) + IEnumHierarchies enumHierarchies = null; + Guid guid = Guid.Empty; + __VSENUMPROJFLAGS flags = __VSENUMPROJFLAGS.EPF_LOADEDINSOLUTION; + ErrorHandler.ThrowOnFailure(this.Solution.GetProjectEnum((uint)flags, ref guid, out enumHierarchies)); + + if(enumHierarchies != null) + { + // Loop projects found + IVsHierarchy[] hierarchy = new IVsHierarchy[1]; + uint fetched = 0; + while(enumHierarchies.Next(1, hierarchy, out fetched) == VSConstants.S_OK && fetched == 1) + { + // If this is a ProjectContainerNode then add to list + IBuildDependencyOnProjectContainer projectNode = hierarchy[0] as IBuildDependencyOnProjectContainer; + if(projectNode != null) + { + projectList.Add(projectNode); + } + } + } + + return projectList; + } + + /// + /// Add build dependency to ProjectContainerNode if IVsBuildDependency is supported by the nested project + /// + /// Project Container where we should add the build dependency + /// Nested project to set a build dependency against + private static void AddBuildDependenyToNestedProject(IBuildDependencyUpdate projectContainer, IVsHierarchy nestedProject) + { + // Validate input + Debug.Assert(projectContainer != null, "Project Container must not be null"); + Debug.Assert(nestedProject != null, "Nested Project must not be null"); + if(projectContainer == null || nestedProject == null) + { + // Invalid argument + return; + } + + // Create new NestedProjectBuildDependency + NestedProjectBuildDependency dependency = new NestedProjectBuildDependency(nestedProject); + projectContainer.AddBuildDependency(dependency); + } + + #endregion + + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/SolutionListenerForProjectEvents.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/SolutionListenerForProjectEvents.cs new file mode 100644 index 0000000000..50cdcc3233 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/SolutionListenerForProjectEvents.cs @@ -0,0 +1,102 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using IServiceProvider = System.IServiceProvider; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// This class triggers the project events for "our" hierrachies. + /// + internal class SolutionListenerForProjectEvents : SolutionListener, IProjectEvents + { + #region events + /// Event raised just after the project file opened. + /// + public event EventHandler AfterProjectFileOpened; + + /// + /// Event raised before the project file closed. + /// + public event EventHandler BeforeProjectFileClosed; + #endregion + + #region ctor + internal SolutionListenerForProjectEvents(IServiceProvider serviceProvider) + : base(serviceProvider) + { + } + #endregion + + #region overridden methods + public override int OnAfterOpenProject(IVsHierarchy hierarchy, int added) + { + IProjectEventsListener projectEventListener = hierarchy as IProjectEventsListener; + if(projectEventListener != null && projectEventListener.IsProjectEventsListener) + { + this.RaiseAfterProjectFileOpened((added != 0) ? true : false); + } + + return VSConstants.S_OK; + } + + public override int OnBeforeCloseProject(IVsHierarchy hierarchy, int removed) + { + IProjectEventsListener projectEvents = hierarchy as IProjectEventsListener; + if(projectEvents != null && projectEvents.IsProjectEventsListener) + { + this.RaiseBeforeProjectFileClosed((removed != 0) ? true : false); + } + + return VSConstants.S_OK; + } + #endregion + + #region helpers + /// + /// Raises after project file opened event. + /// + /// True if the project is added to the solution after the solution is opened. false if the project is added to the solution while the solution is being opened. + private void RaiseAfterProjectFileOpened(bool added) + { + // Save event in temporary variable to avoid race condition. + EventHandler tempEvent = this.AfterProjectFileOpened; + if(tempEvent != null) + { + tempEvent(this, new AfterProjectFileOpenedEventArgs(added)); + } + } + + + + + /// + /// Raises the before project file closed event. + /// + /// true if the project was removed from the solution before the solution was closed. false if the project was removed from the solution while the solution was being closed. + private void RaiseBeforeProjectFileClosed(bool removed) + { + // Save event in temporary variable to avoid race condition. + EventHandler tempEvent = this.BeforeProjectFileClosed; + if(tempEvent != null) + { + tempEvent(this, new BeforeProjectFileClosedEventArgs(removed)); + } + } + } + #endregion +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/SolutionListenerForProjectOpen.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/SolutionListenerForProjectOpen.cs new file mode 100644 index 0000000000..3a1c3ad41f --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/SolutionListenerForProjectOpen.cs @@ -0,0 +1,66 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using IServiceProvider = System.IServiceProvider; + +namespace Microsoft.VisualStudio.Project +{ + + [CLSCompliant(false)] + public class SolutionListenerForProjectOpen : SolutionListener + { + public SolutionListenerForProjectOpen(IServiceProvider serviceProvider) + : base(serviceProvider) + { + } + + public override int OnAfterOpenProject(IVsHierarchy hierarchy, int added) + { + // If this is a new project and our project. We use here that it is only our project that will implemnet the "internal" IBuildDependencyOnProjectContainer. + if(added != 0 && hierarchy is IBuildDependencyUpdate) + { + IVsUIHierarchy uiHierarchy = hierarchy as IVsUIHierarchy; + Debug.Assert(uiHierarchy != null, "The ProjectNode should implement IVsUIHierarchy"); + // Expand and select project node + IVsUIHierarchyWindow uiWindow = UIHierarchyUtilities.GetUIHierarchyWindow(this.ServiceProvider, HierarchyNode.SolutionExplorer); + if(uiWindow != null) + { + __VSHIERARCHYITEMSTATE state; + uint stateAsInt; + if(uiWindow.GetItemState(uiHierarchy, VSConstants.VSITEMID_ROOT, (uint)__VSHIERARCHYITEMSTATE.HIS_Expanded, out stateAsInt) == VSConstants.S_OK) + { + state = (__VSHIERARCHYITEMSTATE)stateAsInt; + if(state != __VSHIERARCHYITEMSTATE.HIS_Expanded) + { + int hr; + hr = uiWindow.ExpandItem(uiHierarchy, VSConstants.VSITEMID_ROOT, EXPANDFLAGS.EXPF_ExpandParentsToShowItem); + if(ErrorHandler.Failed(hr)) + Trace.WriteLine("Failed to expand project node"); + hr = uiWindow.ExpandItem(uiHierarchy, VSConstants.VSITEMID_ROOT, EXPANDFLAGS.EXPF_SelectItem); + if(ErrorHandler.Failed(hr)) + Trace.WriteLine("Failed to select project node"); + + return hr; + } + } + } + } + return VSConstants.S_OK; + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/SolutionListenerForProjectReferenceUpdate.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/SolutionListenerForProjectReferenceUpdate.cs new file mode 100644 index 0000000000..b3812f5618 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/SolutionListenerForProjectReferenceUpdate.cs @@ -0,0 +1,238 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using IServiceProvider = System.IServiceProvider; + +namespace Microsoft.VisualStudio.Project +{ + [CLSCompliant(false)] + public class SolutionListenerForProjectReferenceUpdate : SolutionListener + { + + #region ctor + public SolutionListenerForProjectReferenceUpdate(IServiceProvider serviceProvider) + : base(serviceProvider) + { + } + #endregion + + #region overridden methods + /// + /// Delete this project from the references of projects of this type, if it is found. + /// + /// + /// + /// + public override int OnBeforeCloseProject(IVsHierarchy hierarchy, int removed) + { + if(removed != 0) + { + List projectReferences = this.GetProjectReferencesContainingThisProject(hierarchy); + + foreach(ProjectReferenceNode projectReference in projectReferences) + { + projectReference.Remove(false); + // Set back the remove state on the project refererence. The reason why we are doing this is that the OnBeforeUnloadProject immedaitely calls + // OnBeforeCloseProject, thus we would be deleting references when we should not. Unload should not remove references. + projectReference.CanRemoveReference = true; + } + } + + return VSConstants.S_OK; + } + + + /// + /// Needs to update the dangling reference on projects that contain this hierarchy as project reference. + /// + /// + /// + /// + public override int OnAfterLoadProject(IVsHierarchy stubHierarchy, IVsHierarchy realHierarchy) + { + List projectReferences = this.GetProjectReferencesContainingThisProject(realHierarchy); + + // Refersh the project reference node. That should trigger the drawing of the normal project reference icon. + foreach(ProjectReferenceNode projectReference in projectReferences) + { + projectReference.CanRemoveReference = true; + + projectReference.OnInvalidateItems(projectReference.Parent); + } + + return VSConstants.S_OK; + } + + + public override int OnAfterRenameProject(IVsHierarchy hierarchy) + { + if(hierarchy == null) + { + return VSConstants.E_INVALIDARG; + } + + try + { + List projectReferences = this.GetProjectReferencesContainingThisProject(hierarchy); + + // Collect data that is needed to initialize the new project reference node. + string projectRef; + ErrorHandler.ThrowOnFailure(this.Solution.GetProjrefOfProject(hierarchy, out projectRef)); + + object nameAsObject; + ErrorHandler.ThrowOnFailure(hierarchy.GetProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_Name, out nameAsObject)); + string projectName = (string)nameAsObject; + + string projectPath = String.Empty; + + IVsProject3 project = hierarchy as IVsProject3; + + if(project != null) + { + ErrorHandler.ThrowOnFailure(project.GetMkDocument(VSConstants.VSITEMID_ROOT, out projectPath)); + projectPath = Path.GetDirectoryName(projectPath); + } + + // Remove and re add the node. + foreach(ProjectReferenceNode projectReference in projectReferences) + { + ProjectNode projectMgr = projectReference.ProjectMgr; + IReferenceContainer refContainer = projectMgr.GetReferenceContainer(); + projectReference.Remove(false); + + VSCOMPONENTSELECTORDATA selectorData = new VSCOMPONENTSELECTORDATA(); + selectorData.type = VSCOMPONENTTYPE.VSCOMPONENTTYPE_Project; + selectorData.bstrTitle = projectName; + selectorData.bstrFile = projectPath; + selectorData.bstrProjRef = projectRef; + refContainer.AddReferenceFromSelectorData(selectorData); + } + } + catch(COMException e) + { + return e.ErrorCode; + } + + return VSConstants.S_OK; + } + + + public override int OnBeforeUnloadProject(IVsHierarchy realHierarchy, IVsHierarchy stubHierarchy) + { + List projectReferences = this.GetProjectReferencesContainingThisProject(realHierarchy); + + // Refresh the project reference node. That should trigger the drawing of the dangling project reference icon. + foreach(ProjectReferenceNode projectReference in projectReferences) + { + projectReference.IsNodeValid = true; + projectReference.OnInvalidateItems(projectReference.Parent); + projectReference.CanRemoveReference = false; + projectReference.IsNodeValid = false; + projectReference.ReferencedProjectObject = null; + } + + return VSConstants.S_OK; + + } + + #endregion + + #region helper methods + private List GetProjectReferencesContainingThisProject(IVsHierarchy inputHierarchy) + { + List projectReferences = new List(); + if(this.Solution == null || inputHierarchy == null) + { + return projectReferences; + } + + uint flags = (uint)(__VSENUMPROJFLAGS.EPF_ALLPROJECTS | __VSENUMPROJFLAGS.EPF_LOADEDINSOLUTION); + Guid enumOnlyThisType = Guid.Empty; + IEnumHierarchies enumHierarchies = null; + + ErrorHandler.ThrowOnFailure(this.Solution.GetProjectEnum(flags, ref enumOnlyThisType, out enumHierarchies)); + Debug.Assert(enumHierarchies != null, "Could not get list of hierarchies in solution"); + + IVsHierarchy[] hierarchies = new IVsHierarchy[1]; + uint fetched; + int returnValue = VSConstants.S_OK; + do + { + returnValue = enumHierarchies.Next(1, hierarchies, out fetched); + Debug.Assert(fetched <= 1, "We asked one project to be fetched VSCore gave more than one. We cannot handle that"); + if(returnValue == VSConstants.S_OK && fetched == 1) + { + IVsHierarchy hierarchy = hierarchies[0]; + Debug.Assert(hierarchy != null, "Could not retrieve a hierarchy"); + IReferenceContainerProvider provider = hierarchy as IReferenceContainerProvider; + if(provider != null) + { + IReferenceContainer referenceContainer = provider.GetReferenceContainer(); + + Debug.Assert(referenceContainer != null, "Could not found the References virtual node"); + ProjectReferenceNode projectReferenceNode = GetProjectReferenceOnNodeForHierarchy(referenceContainer.EnumReferences(), inputHierarchy); + if(projectReferenceNode != null) + { + projectReferences.Add(projectReferenceNode); + } + } + } + } while(returnValue == VSConstants.S_OK && fetched == 1); + + return projectReferences; + } + + private static ProjectReferenceNode GetProjectReferenceOnNodeForHierarchy(IList references, IVsHierarchy inputHierarchy) + { + if(references == null) + { + return null; + } + + Guid projectGuid; + ErrorHandler.ThrowOnFailure(inputHierarchy.GetGuidProperty(VSConstants.VSITEMID_ROOT, (int)__VSHPROPID.VSHPROPID_ProjectIDGuid, out projectGuid)); + + string canonicalName; + ErrorHandler.ThrowOnFailure(inputHierarchy.GetCanonicalName(VSConstants.VSITEMID_ROOT, out canonicalName)); + foreach(ReferenceNode refNode in references) + { + ProjectReferenceNode projRefNode = refNode as ProjectReferenceNode; + if(projRefNode != null) + { + if(projRefNode.ReferencedProjectGuid == projectGuid) + { + return projRefNode; + } + + // Try with canonical names, if the project that is removed is an unloaded project than the above criteria will not pass. + if(!String.IsNullOrEmpty(projRefNode.Url) && NativeMethods.IsSamePath(projRefNode.Url, canonicalName)) + { + return projRefNode; + } + } + } + + return null; + + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/StructuresEnums.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/StructuresEnums.cs new file mode 100644 index 0000000000..0fe81fd7dd --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/StructuresEnums.cs @@ -0,0 +1,562 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics.CodeAnalysis; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.VisualStudio.Project +{ + #region structures + [StructLayoutAttribute(LayoutKind.Sequential)] + internal struct _DROPFILES + { + public Int32 pFiles; + public Int32 X; + public Int32 Y; + public Int32 fNC; + public Int32 fWide; + } + #endregion + + #region enums + + /// + /// The type of build performed. + /// + public enum BuildKind + { + Sync, + Async + } + + /// + /// Defines possible types of output that can produced by a language project + /// + [PropertyPageTypeConverterAttribute(typeof(OutputTypeConverter))] + public enum OutputType + { + /// + /// The output type is a class library. + /// + Library, + + /// + /// The output type is a windows executable. + /// + WinExe, + + /// + /// The output type is an executable. + /// + Exe + } + + /// + /// Debug values used by DebugModeConverter. + /// + [PropertyPageTypeConverterAttribute(typeof(DebugModeConverter))] + public enum DebugMode + { + Project, + Program, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "URL")] + URL + } + + /// + /// An enumeration that describes the type of action to be taken by the build. + /// + [PropertyPageTypeConverterAttribute(typeof(BuildActionConverter))] + public enum BuildAction + { + None, + Compile, + Content, + EmbeddedResource + } + + /// + /// Defines the version of the CLR that is appropriate to the project. + /// + [PropertyPageTypeConverterAttribute(typeof(PlatformTypeConverter))] + public enum PlatformType + { + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "not")] + notSpecified, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "v")] + v1, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "v")] + v11, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "v")] + v2, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "v")] + v3, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "v")] + v35, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "v")] + v4, + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "cli")] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "cli")] + cli1 + } + + /// + /// Defines the currect state of a property page. + /// + [Flags] + public enum PropPageStatus + { + + Dirty = 0x1, + + Validate = 0x2, + + Clean = 0x4 + } + + [Flags] + [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue")] + public enum ModuleKindFlags + { + + ConsoleApplication, + + WindowsApplication, + + DynamicallyLinkedLibrary, + + ManifestResourceFile, + + UnmanagedDynamicallyLinkedLibrary + } + + /// + /// Defines the status of the command being queried + /// + [Flags] + [SuppressMessage("Microsoft.Naming", "CA1714:FlagsEnumsShouldHavePluralNames")] + [SuppressMessage("Microsoft.Design", "CA1008:EnumsShouldHaveZeroValue")] + public enum QueryStatusResult + { + /// + /// The command is not supported. + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "NOTSUPPORTED")] + NOTSUPPORTED = 0, + + /// + /// The command is supported + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "SUPPORTED")] + SUPPORTED = 1, + + /// + /// The command is enabled + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ENABLED")] + ENABLED = 2, + + /// + /// The command is toggled on + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "LATCHED")] + LATCHED = 4, + + /// + /// The command is toggled off (the opposite of LATCHED). + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "NINCHED")] + NINCHED = 8, + + /// + /// The command is invisible. + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "INVISIBLE")] + INVISIBLE = 16 + } + + /// + /// Defines the type of item to be added to the hierarchy. + /// + public enum HierarchyAddType + { + AddNewItem, + AddExistingItem + } + + /// + /// Defines the component from which a command was issued. + /// + public enum CommandOrigin + { + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ui")] + UiHierarchy, + OleCommandTarget + } + + /// + /// Defines the current status of the build process. + /// + public enum MSBuildResult + { + /// + /// The build is currently suspended. + /// + Suspended, + + /// + /// The build has been restarted. + /// + Resumed, + + /// + /// The build failed. + /// + Failed, + + /// + /// The build was successful. + /// + Successful, + } + + /// + /// Defines the type of action to be taken in showing the window frame. + /// + public enum WindowFrameShowAction + { + DoNotShow, + Show, + ShowNoActivate, + Hide, + } + + /// + /// Defines drop types + /// + internal enum DropDataType + { + None, + Shell, + VsStg, + VsRef + } + + /// + /// Used by the hierarchy node to decide which element to redraw. + /// + [Flags] + [SuppressMessage("Microsoft.Naming", "CA1714:FlagsEnumsShouldHavePluralNames")] + public enum UIHierarchyElement + { + None = 0, + + /// + /// This will be translated to VSHPROPID_IconIndex + /// + Icon = 1, + + /// + /// This will be translated to VSHPROPID_StateIconIndex + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Scc")] + SccState = 2, + + /// + /// This will be translated to VSHPROPID_Caption + /// + Caption = 4 + } + + /// + /// Defines the global propeties used by the msbuild project. + /// + public enum GlobalProperty + { + /// + /// Property specifying that we are building inside VS. + /// + BuildingInsideVisualStudio, + + /// + /// The VS installation directory. This is the same as the $(DevEnvDir) macro. + /// + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Env")] + DevEnvDir, + + /// + /// The name of the solution the project is created. This is the same as the $(SolutionName) macro. + /// + SolutionName, + + /// + /// The file name of the solution. This is the same as $(SolutionFileName) macro. + /// + SolutionFileName, + + /// + /// The full path of the solution. This is the same as the $(SolutionPath) macro. + /// + SolutionPath, + + /// + /// The directory of the solution. This is the same as the $(SolutionDir) macro. + /// + SolutionDir, + + /// + /// The extension of teh directory. This is the same as the $(SolutionExt) macro. + /// + SolutionExt, + + /// + /// The fxcop installation directory. + /// + FxCopDir, + + /// + /// The ResolvedNonMSBuildProjectOutputs msbuild property + /// + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "VSIDE")] + VSIDEResolvedNonMSBuildProjectOutputs, + + /// + /// The Configuartion property. + /// + Configuration, + + /// + /// The platform property. + /// + Platform, + + /// + /// The RunCodeAnalysisOnce property + /// + RunCodeAnalysisOnce, + + /// + /// The VisualStudioStyleErrors property + /// + VisualStudioStyleErrors, + } + #endregion + + public class AfterProjectFileOpenedEventArgs : EventArgs + { + #region fields + private bool added; + #endregion + + #region properties + /// + /// True if the project is added to the solution after the solution is opened. false if the project is added to the solution while the solution is being opened. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal bool Added + { + get { return this.added; } + } + #endregion + + #region ctor + internal AfterProjectFileOpenedEventArgs(bool added) + { + this.added = added; + } + #endregion + } + + public class BeforeProjectFileClosedEventArgs : EventArgs + { + #region fields + private bool removed; + #endregion + + #region properties + /// + /// true if the project was removed from the solution before the solution was closed. false if the project was removed from the solution while the solution was being closed. + /// + [SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal bool Removed + { + get { return this.removed; } + } + #endregion + + #region ctor + internal BeforeProjectFileClosedEventArgs(bool removed) + { + this.removed = removed; + } + #endregion + } + + /// + /// This class is used for the events raised by a HierarchyNode object. + /// + internal class HierarchyNodeEventArgs : EventArgs + { + private HierarchyNode child; + + internal HierarchyNodeEventArgs(HierarchyNode child) + { + this.child = child; + } + + public HierarchyNode Child + { + get { return this.child; } + } + } + + /// + /// Event args class for triggering file change event arguments. + /// + internal class FileChangedOnDiskEventArgs : EventArgs + { + #region Private fields + /// + /// File name that was changed on disk. + /// + private string fileName; + + /// + /// The item ide of the file that has changed. + /// + private uint itemID; + + /// + /// The reason the file has changed on disk. + /// + private _VSFILECHANGEFLAGS fileChangeFlag; + #endregion + + /// + /// Constructs a new event args. + /// + /// File name that was changed on disk. + /// The item id of the file that was changed on disk. + internal FileChangedOnDiskEventArgs(string fileName, uint id, _VSFILECHANGEFLAGS flag) + { + this.fileName = fileName; + this.itemID = id; + this.fileChangeFlag = flag; + } + + /// + /// Gets the file name that was changed on disk. + /// + /// The file that was changed on disk. + internal string FileName + { + get + { + return this.fileName; + } + } + + /// + /// Gets item id of the file that has changed + /// + /// The file that was changed on disk. + internal uint ItemID + { + get + { + return this.itemID; + } + } + + /// + /// The reason while the file has chnaged on disk. + /// + /// The reason while the file has chnaged on disk. + internal _VSFILECHANGEFLAGS FileChangeFlag + { + get + { + return this.fileChangeFlag; + } + } + } + + /// + /// Defines the event args for the active configuration chnage event. + /// + public class ActiveConfigurationChangedEventArgs : EventArgs + { + #region Private fields + /// + /// The hierarchy whose configuration has changed + /// + private IVsHierarchy hierarchy; + #endregion + + /// + /// Constructs a new event args. + /// + /// The hierarchy that has changed its configuration. + internal ActiveConfigurationChangedEventArgs(IVsHierarchy hierarchy) + { + this.hierarchy = hierarchy; + } + + /// + /// The hierarchy whose configuration has changed + /// + internal IVsHierarchy Hierarchy + { + get + { + return this.hierarchy; + } + } + } + + /// + /// Argument of the event raised when a project property is changed. + /// + [SuppressMessage("Microsoft.Naming", "CA1710:IdentifiersShouldHaveCorrectSuffix")] + public class ProjectPropertyChangedArgs : EventArgs + { + private string propertyName; + private string oldValue; + private string newValue; + + internal ProjectPropertyChangedArgs(string propertyName, string oldValue, string newValue) + { + this.propertyName = propertyName; + this.oldValue = oldValue; + this.newValue = newValue; + } + + public string NewValue + { + get { return newValue; } + } + + public string OldValue + { + get { return oldValue; } + } + + public string PropertyName + { + get { return propertyName; } + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/SuspendFileChanges.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/SuspendFileChanges.cs new file mode 100644 index 0000000000..19cf5fbdbe --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/SuspendFileChanges.cs @@ -0,0 +1,127 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Runtime.InteropServices; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using IServiceProvider = System.IServiceProvider; +using ShellConstants = Microsoft.VisualStudio.Shell.Interop.Constants; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// helper to make the editor ignore external changes + /// + internal class SuspendFileChanges + { + private string documentFileName; + + private bool isSuspending; + + private IServiceProvider site; + + private IVsDocDataFileChangeControl fileChangeControl; + + public SuspendFileChanges(IServiceProvider site, string document) + { + this.site = site; + this.documentFileName = document; + } + + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily")] + public void Suspend() + { + if(this.isSuspending) + return; + + IntPtr docData = IntPtr.Zero; + try + { + IVsRunningDocumentTable rdt = this.site.GetService(typeof(SVsRunningDocumentTable)) as IVsRunningDocumentTable; + + IVsHierarchy hierarchy; + uint itemId; + uint docCookie; + IVsFileChangeEx fileChange; + + + if(rdt == null) return; + + ErrorHandler.ThrowOnFailure(rdt.FindAndLockDocument((uint)_VSRDTFLAGS.RDT_NoLock, this.documentFileName, out hierarchy, out itemId, out docData, out docCookie)); + + if((docCookie == (uint)ShellConstants.VSDOCCOOKIE_NIL) || docData == IntPtr.Zero) + return; + + fileChange = this.site.GetService(typeof(SVsFileChangeEx)) as IVsFileChangeEx; + + if(fileChange != null) + { + this.isSuspending = true; + ErrorHandler.ThrowOnFailure(fileChange.IgnoreFile(0, this.documentFileName, 1)); + if(docData != IntPtr.Zero) + { + IVsPersistDocData persistDocData = null; + + // if interface is not supported, return null + object unknown = Marshal.GetObjectForIUnknown(docData); + if(unknown is IVsPersistDocData) + { + persistDocData = (IVsPersistDocData)unknown; + if(persistDocData is IVsDocDataFileChangeControl) + { + this.fileChangeControl = (IVsDocDataFileChangeControl)persistDocData; + if(this.fileChangeControl != null) + { + ErrorHandler.ThrowOnFailure(this.fileChangeControl.IgnoreFileChanges(1)); + } + } + } + } + } + } + catch(InvalidCastException e) + { + Trace.WriteLine("Exception" + e.Message); + } + finally + { + if(docData != IntPtr.Zero) + { + Marshal.Release(docData); + } + } + return; + } + + public void Resume() + { + if(!this.isSuspending) + return; + IVsFileChangeEx fileChange; + fileChange = this.site.GetService(typeof(SVsFileChangeEx)) as IVsFileChangeEx; + if(fileChange != null) + { + this.isSuspending = false; + ErrorHandler.ThrowOnFailure(fileChange.IgnoreFile(0, this.documentFileName, 0)); + if(this.fileChangeControl != null) + { + ErrorHandler.ThrowOnFailure(this.fileChangeControl.IgnoreFileChanges(0)); + } + } + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/TargetCollectionShim.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/TargetCollectionShim.cs new file mode 100644 index 0000000000..e85ad18d05 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/TargetCollectionShim.cs @@ -0,0 +1,37 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Build.BuildEngine; + +namespace Microsoft.VisualStudio.Project { + class TargetCollectionShim : IEnumerable { + private Build.BuildEngine.TargetCollection targetCollection; + + public TargetCollectionShim(Build.BuildEngine.TargetCollection targetCollection) { + this.targetCollection = targetCollection; + } + + + + #region IEnumerable Members + + public IEnumerator GetEnumerator() { + foreach (var v in targetCollection) { + yield return new TargetShim((Target)v); + } + } + + #endregion + + #region IEnumerable Members + + System.Collections.IEnumerator System.Collections.IEnumerable.GetEnumerator() { + foreach (var v in targetCollection) { + yield return new TargetShim((Target)v); + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/TargetShim.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/TargetShim.cs new file mode 100644 index 0000000000..350441a3d1 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/TargetShim.cs @@ -0,0 +1,19 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.Build.BuildEngine; + +namespace Microsoft.VisualStudio.Project { + class TargetShim { + private Target _target; + + public TargetShim(Target target) { + _target = target; + } + + public string Name { get { return _target.Name; } } + + public bool IsImported { get { return _target.IsImported; } } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/TokenProcessor.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/TokenProcessor.cs new file mode 100644 index 0000000000..8b3383119b --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/TokenProcessor.cs @@ -0,0 +1,536 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Globalization; +using System.IO; +using System.Text; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Replacement type + /// + public enum TokenReplaceType + { + ReplaceString, + ReplaceNumber, + ReplaceCode + } + + /// + /// Contain a number of functions that handle token replacement + /// + [CLSCompliant(false)] + public class TokenProcessor + { + #region fields + // Internal fields + private ArrayList tokenlist; + + + #endregion + + #region Initialization + /// + /// Constructor + /// + public TokenProcessor() + { + tokenlist = new ArrayList(); + } + + /// + /// Reset list of TokenReplacer entries + /// + public virtual void Reset() + { + tokenlist.Clear(); + } + + + /// + /// Add a replacement type entry + /// + /// token to replace + /// replacement string + public virtual void AddReplace(string token, string replacement) + { + tokenlist.Add(new ReplacePairToken(token, replacement)); + } + + /// + /// Add replace between entry + /// + /// Start token + /// End token + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "tokenid")] + public virtual void AddReplaceBetween(string tokenid, string tokenStart, string tokenEnd, string replacement) + { + tokenlist.Add(new ReplaceBetweenPairToken(tokenid, tokenStart, tokenEnd, replacement)); + } + + /// + /// Add a deletion entry + /// + /// Token to delete + public virtual void AddDelete(string tokenToDelete) + { + tokenlist.Add(new DeleteToken(tokenToDelete)); + } + #endregion + + #region TokenProcessing + /// + /// For all known token, replace token with correct value + /// + /// File of the source file + /// File of the destination file + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1800:DoNotCastUnnecessarily"), SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Untoken")] + public virtual void UntokenFile(string source, string destination) + { + if(string.IsNullOrEmpty(source)) + throw new ArgumentNullException("source"); + + if(string.IsNullOrEmpty(destination)) + throw new ArgumentNullException("destination"); + + // Make sure that the destination folder exists. + string destinationFolder = Path.GetDirectoryName(destination); + if(!Directory.Exists(destinationFolder)) + { + Directory.CreateDirectory(destinationFolder); + } + + //Open the file. Check to see if the File is binary or text. + // NOTE: This is not correct because GetBinaryType will return true + // only if the file is executable, not if it is a dll, a library or + // any other type of binary file. + + uint binaryType; + if(!NativeMethods.GetBinaryType(source, out binaryType)) + { + Encoding encoding = Encoding.Default; + string buffer = null; + // Create the reader to get the text. Note that we will default to ASCII as + // encoding if the file does not contains a different signature. + using(StreamReader reader = new StreamReader(source, Encoding.ASCII, true)) + { + // Get the content of the file. + buffer = reader.ReadToEnd(); + // Detect the encoding of the source file. Note that we + // can get the encoding only after a read operation is + // performed on the file. + encoding = reader.CurrentEncoding; + } + foreach(object pair in tokenlist) + { + if(pair is DeleteToken) + DeleteTokens(ref buffer, (DeleteToken)pair); + if(pair is ReplaceBetweenPairToken) + ReplaceBetweenTokens(ref buffer, (ReplaceBetweenPairToken)pair); + if(pair is ReplacePairToken) + ReplaceTokens(ref buffer, (ReplacePairToken)pair); + } + File.WriteAllText(destination, buffer, encoding); + } + else + File.Copy(source, destination); + + } + + /// + /// Replaces the tokens in a buffer with the replacement string + /// + /// Buffer to update + /// replacement data + public virtual void ReplaceTokens(ref string buffer, ReplacePairToken tokenToReplace) + { + if (tokenToReplace == null) + { + throw new ArgumentNullException("tokenToReplace"); + } + + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + buffer = buffer.Replace(tokenToReplace.Token, tokenToReplace.Replacement); + } + + /// + /// Deletes the token from the buffer + /// + /// Buffer to update + /// token to delete + public virtual void DeleteTokens(ref string buffer, DeleteToken tokenToDelete) + { + if (tokenToDelete == null) + { + throw new ArgumentNullException("tokenToDelete"); + } + + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + buffer = buffer.Replace(tokenToDelete.StringToDelete, string.Empty); + } + + /// + /// Replaces the token from the buffer between the provided tokens + /// + /// Buffer to update + /// replacement token + public virtual void ReplaceBetweenTokens(ref string buffer, ReplaceBetweenPairToken rpBetweenToken) + { + if (rpBetweenToken == null) + { + throw new ArgumentNullException("rpBetweenToken"); + } + + if (buffer == null) + { + throw new ArgumentNullException("buffer"); + } + + string regularExp = rpBetweenToken.TokenStart + "[^" + rpBetweenToken.TokenIdentifier + "]*" + rpBetweenToken.TokenEnd; + buffer = System.Text.RegularExpressions.Regex.Replace(buffer, regularExp, rpBetweenToken.TokenReplacement); + } + + #endregion + + #region Guid generators + /// + /// Generates a string representation of a guid with the following format: + /// 0x01020304, 0x0506, 0x0708, 0x09, 0x0A, 0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10 + /// + /// Guid to be generated + /// The guid as string + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] + public string GuidToForm1(Guid value) + { + byte[] GuidBytes = value.ToByteArray(); + StringBuilder ResultingStr = new StringBuilder(80); + + // First 4 bytes + int i = 0; + int Number = 0; + for(i = 0; i < 4; ++i) + { + int CurrentByte = GuidBytes[i]; + Number += CurrentByte << (8 * i); + } + UInt32 FourBytes = (UInt32)Number; + ResultingStr.AppendFormat(CultureInfo.InvariantCulture, "0x{0}", FourBytes.ToString("X", CultureInfo.InvariantCulture)); + + // 2 chunks of 2 bytes + for(int j = 0; j < 2; ++j) + { + Number = 0; + for(int k = 0; k < 2; ++k) + { + int CurrentByte = GuidBytes[i++]; + Number += CurrentByte << (8 * k); + } + UInt16 TwoBytes = (UInt16)Number; + ResultingStr.AppendFormat(CultureInfo.InvariantCulture, ", 0x{0}", TwoBytes.ToString("X", CultureInfo.InvariantCulture)); + } + + // 8 chunks of 1 bytes + for(int j = 0; j < 8; ++j) + { + ResultingStr.AppendFormat(CultureInfo.InvariantCulture, ", 0x{0}", GuidBytes[i++].ToString("X", CultureInfo.InvariantCulture)); + } + + return ResultingStr.ToString(); + } + #endregion + + #region Helper Methods + /// + /// This function will accept a subset of the characters that can create an + /// identifier name: there are other unicode char that can be inside the name, but + /// this function will not allow. By now it can work this way, but when and if the + /// VSIP package will handle also languages different from english, this function + /// must be changed. + /// + /// Character to validate + /// true if successful false otherwise + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "c")] + protected static bool IsValidIdentifierChar(char c) + { + if((c >= 'a') && (c <= 'z')) + { + return true; + } + if((c >= 'A') && (c <= 'Z')) + { + return true; + } + if(c == '_') + { + return true; + } + if((c >= '0') && (c <= '9')) + { + return true; + } + + return false; + } + + /// + /// Verifies if the start character is valid + /// + /// Start character + /// true if successful false otherwise + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "c")] + protected static bool IsValidIdentifierStartChar(char c) + { + if(!IsValidIdentifierChar(c)) + { + return false; + } + if((c >= '0') && (c <= '9')) + { + return false; + } + + return true; + } + + /// + /// The goal here is to reduce the risk of name conflict between 2 classes + /// added in different directories. This code does NOT garanty uniqueness. + /// To garanty uniqueness, you should change this function to work with + /// the language service to verify that the namespace+class generated does + /// not conflict. + /// + /// Full path to the new file + /// Namespace to use for the new file + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1822:MarkMembersAsStatic")] + public string GetFileNamespace(string fileFullPath, ProjectNode node) + { + if (node == null) + { + throw new ArgumentNullException("node"); + } + + // Get base namespace from the project + string namespce = node.GetProjectProperty("RootNamespace"); + if(String.IsNullOrEmpty(namespce)) + namespce = Path.GetFileNameWithoutExtension(fileFullPath); ; + + // If the item is added to a subfolder, the name space should reflect this. + // This is done so that class names from 2 files with the same name but different + // directories don't conflict. + string relativePath = Path.GetDirectoryName(fileFullPath); + string projectPath = Path.GetDirectoryName(node.GetMkDocument()); + // Our project system only support adding files that are sibling of the project file or that are in subdirectories. + if(String.Compare(projectPath, 0, relativePath, 0, projectPath.Length, true, CultureInfo.CurrentCulture) == 0) + { + relativePath = relativePath.Substring(projectPath.Length); + } + else + { + Debug.Fail("Adding an item to the project that is NOT under the project folder."); + // We are going to use the full file path for generating the namespace + } + + // Get the list of parts + int index = 0; + string[] pathParts; + pathParts = relativePath.Split(Path.DirectorySeparatorChar); + + // Use a string builder with default size being the expected size + StringBuilder result = new StringBuilder(namespce, namespce.Length + relativePath.Length + 1); + // For each path part + while(index < pathParts.Length) + { + string part = pathParts[index]; + ++index; + + // This could happen if the path had leading/trailing slash, we want to ignore empty pieces + if(String.IsNullOrEmpty(part)) + continue; + + // If we reach here, we will be adding something, so add a namespace separator '.' + result.Append('.'); + + // Make sure it starts with a letter + if(!char.IsLetter(part, 0)) + result.Append('N'); + + // Filter invalid namespace characters + foreach(char c in part) + { + if(char.IsLetterOrDigit(c)) + result.Append(c); + } + } + return result.ToString(); + } + #endregion + + } + + /// + /// Storage classes for replacement tokens + /// + public class ReplacePairToken + { + /// + /// token string + /// + private string token; + + /// + /// Replacement string + /// + private string replacement; + + /// + /// Constructor + /// + /// replaceable token + /// replacement string + public ReplacePairToken(string token, string replacement) + { + this.token = token; + this.replacement = replacement; + } + + /// + /// Token that needs to be replaced + /// + public string Token + { + get { return token; } + } + /// + /// String to replace the token with + /// + public string Replacement + { + get { return replacement; } + } + } + + /// + /// Storage classes for token to be deleted + /// + public class DeleteToken + { + /// + /// String to delete + /// + private string token; + + /// + /// Constructor + /// + /// Deletable token. + public DeleteToken(string token) + { + this.token = token; + } + + /// + /// Token marking the end of the block to delete + /// + public string StringToDelete + { + get { return token; } + } + } + + /// + /// Storage classes for string to be deleted between tokens to be deleted + /// + public class ReplaceBetweenPairToken + { + /// + /// Token start + /// + private string tokenStart; + + /// + /// End token + /// + private string tokenEnd; + + /// + /// Replacement string + /// + private string replacement; + + /// + /// Token identifier string + /// + private string tokenidentifier; + + /// + /// Constructor + /// + /// Start token + /// End Token + /// Replacement string. + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "tokenid")] + public ReplaceBetweenPairToken(string tokenid, string blockStart, string blockEnd, string replacement) + { + tokenStart = blockStart; + tokenEnd = blockEnd; + this.replacement = replacement; + tokenidentifier = tokenid; + } + + /// + /// Token marking the begining of the block to delete + /// + public string TokenStart + { + get { return tokenStart; } + } + + /// + /// Token marking the end of the block to delete + /// + public string TokenEnd + { + get { return tokenEnd; } + } + + /// + /// Token marking the end of the block to delete + /// + public string TokenReplacement + { + get { return replacement; } + } + + /// + /// Token Identifier + /// + public string TokenIdentifier + { + get { return tokenidentifier; } + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Tracing.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Tracing.cs new file mode 100644 index 0000000000..5a92d1bace --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Tracing.cs @@ -0,0 +1,77 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Diagnostics; + +namespace Microsoft.VisualStudio.Project +{ + internal class CCITracing + { + private CCITracing() { } + + [ConditionalAttribute("Enable_CCIDiagnostics")] + static void InternalTraceCall(int levels) + { + System.Diagnostics.StackFrame stack; + stack = new System.Diagnostics.StackFrame(levels); + System.Reflection.MethodBase method = stack.GetMethod(); + if(method != null) + { + string name = method.Name + " \tin class " + method.DeclaringType.Name; + System.Diagnostics.Trace.WriteLine("Call Trace: \t" + name); + } + } + + [ConditionalAttribute("CCI_TRACING")] + static public void TraceCall() + { + // skip this one as well + CCITracing.InternalTraceCall(2); + } + + [ConditionalAttribute("CCI_TRACING")] + static public void TraceCall(string strParameters) + { + CCITracing.InternalTraceCall(2); + System.Diagnostics.Trace.WriteLine("\tParameters: \t" + strParameters); + } + + [ConditionalAttribute("CCI_TRACING")] + static public void Trace(System.Exception e) + { + CCITracing.InternalTraceCall(2); + System.Diagnostics.Trace.WriteLine("ExceptionInfo: \t" + e.ToString()); + } + + [ConditionalAttribute("CCI_TRACING")] + static public void Trace(string strOutput) + { + System.Diagnostics.Trace.WriteLine(strOutput); + } + + [ConditionalAttribute("CCI_TRACING")] + static public void TraceData(string strOutput) + { + System.Diagnostics.Trace.WriteLine("Data Trace: \t" + strOutput); + } + + [ConditionalAttribute("Enable_CCIFileOutput")] + [ConditionalAttribute("CCI_TRACING")] + static public void AddTraceLog(string strFileName) + { + TextWriterTraceListener tw = new TextWriterTraceListener("c:\\mytrace.log"); + System.Diagnostics.Trace.Listeners.Add(tw); + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/TrackDocumentsHelper.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/TrackDocumentsHelper.cs new file mode 100644 index 0000000000..d91bc69927 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/TrackDocumentsHelper.cs @@ -0,0 +1,177 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Used by a project to query the environment for permission to add, remove, or rename a file or directory in a solution + /// + internal class TrackDocumentsHelper + { + #region fields + private ProjectNode projectMgr; + #endregion + + #region properties + + #endregion + + #region ctors + internal TrackDocumentsHelper(ProjectNode project) + { + this.projectMgr = project; + } + #endregion + + #region helper methods + /// + /// Gets the IVsTrackProjectDocuments2 object by asking the service provider for it. + /// + /// the IVsTrackProjectDocuments2 object + private IVsTrackProjectDocuments2 GetIVsTrackProjectDocuments2() + { + Debug.Assert(this.projectMgr != null && !this.projectMgr.IsClosed && this.projectMgr.Site != null); + + IVsTrackProjectDocuments2 documentTracker = this.projectMgr.Site.GetService(typeof(SVsTrackProjectDocuments)) as IVsTrackProjectDocuments2; + if(documentTracker == null) + { + throw new InvalidOperationException(); + } + + return documentTracker; + } + + /// + /// Asks the environment for permission to add files. + /// + /// The files to add. + /// The VSQUERYADDFILEFLAGS flags associated to the files added + /// true if the file can be added, false if not. + internal bool CanAddItems(string[] files, VSQUERYADDFILEFLAGS[] flags) + { + // If we are silent then we assume that the file can be added, since we do not want to trigger this event. + if((this.projectMgr.EventTriggeringFlag & ProjectNode.EventTriggering.DoNotTriggerTrackerEvents) != 0) + { + return true; + } + + if(files == null || files.Length == 0) + { + return false; + } + + int len = files.Length; + VSQUERYADDFILERESULTS[] summary = new VSQUERYADDFILERESULTS[1]; + ErrorHandler.ThrowOnFailure(this.GetIVsTrackProjectDocuments2().OnQueryAddFiles(this.projectMgr, len, files, flags, summary, null)); + if(summary[0] == VSQUERYADDFILERESULTS.VSQUERYADDFILERESULTS_AddNotOK) + { + return false; + } + + return true; + } + + /// + /// Notify the environment about a file just added + /// + internal void OnItemAdded(string file, VSADDFILEFLAGS flag) + { + if((this.projectMgr.EventTriggeringFlag & ProjectNode.EventTriggering.DoNotTriggerTrackerEvents) == 0) + { + ErrorHandler.ThrowOnFailure(this.GetIVsTrackProjectDocuments2().OnAfterAddFilesEx(this.projectMgr, 1, new string[1] { file }, new VSADDFILEFLAGS[1] { flag })); + } + } + + /// + /// Asks the environment for permission to remove files. + /// + /// an array of files to remove + /// The VSQUERYREMOVEFILEFLAGS associated to the files to be removed. + /// true if the files can be removed, false if not. + internal bool CanRemoveItems(string[] files, VSQUERYREMOVEFILEFLAGS[] flags) + { + // If we are silent then we assume that the file can be removed, since we do not want to trigger this event. + if((this.projectMgr.EventTriggeringFlag & ProjectNode.EventTriggering.DoNotTriggerTrackerEvents) != 0) + { + return true; + } + + if(files == null || files.Length == 0) + { + return false; + } + int length = files.Length; + + VSQUERYREMOVEFILERESULTS[] summary = new VSQUERYREMOVEFILERESULTS[1]; + + ErrorHandler.ThrowOnFailure(this.GetIVsTrackProjectDocuments2().OnQueryRemoveFiles(this.projectMgr, length, files, flags, summary, null)); + if(summary[0] == VSQUERYREMOVEFILERESULTS.VSQUERYREMOVEFILERESULTS_RemoveNotOK) + { + return false; + } + + return true; + } + + /// + /// Notify the environment about a file just removed + /// + internal void OnItemRemoved(string file, VSREMOVEFILEFLAGS flag) + { + if((this.projectMgr.EventTriggeringFlag & ProjectNode.EventTriggering.DoNotTriggerTrackerEvents) == 0) + { + ErrorHandler.ThrowOnFailure(this.GetIVsTrackProjectDocuments2().OnAfterRemoveFiles(this.projectMgr, 1, new string[1] { file }, new VSREMOVEFILEFLAGS[1] { flag })); + } + } + + /// + /// Asks the environment for permission to rename files. + /// + /// Path to the file to be renamed. + /// Path to the new file. + /// The VSRENAMEFILEFLAGS associated with the file to be renamed. + /// true if the file can be renamed. Otherwise false. + internal bool CanRenameItem(string oldFileName, string newFileName, VSRENAMEFILEFLAGS flag) + { + // If we are silent then we assume that the file can be renamed, since we do not want to trigger this event. + if((this.projectMgr.EventTriggeringFlag & ProjectNode.EventTriggering.DoNotTriggerTrackerEvents) != 0) + { + return true; + } + + int iCanContinue = 0; + ErrorHandler.ThrowOnFailure(this.GetIVsTrackProjectDocuments2().OnQueryRenameFile(this.projectMgr, oldFileName, newFileName, flag, out iCanContinue)); + return (iCanContinue != 0); + } + + /// + /// Get's called to tell the env that a file was renamed + /// + /// + internal void OnItemRenamed(string strOldName, string strNewName, VSRENAMEFILEFLAGS flag) + { + if((this.projectMgr.EventTriggeringFlag & ProjectNode.EventTriggering.DoNotTriggerTrackerEvents) == 0) + { + ErrorHandler.ThrowOnFailure(this.GetIVsTrackProjectDocuments2().OnAfterRenameFile(this.projectMgr, strOldName, strNewName, flag)); + } + } + #endregion + } +} + diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/TypeConverters.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/TypeConverters.cs new file mode 100644 index 0000000000..aeefc5a3fe --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/TypeConverters.cs @@ -0,0 +1,289 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel; +using System.Globalization; + +namespace Microsoft.VisualStudio.Project +{ + public class OutputTypeConverter : EnumConverter + { + public OutputTypeConverter() + : base(typeof(OutputType)) + { + + } + + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if(sourceType == typeof(string)) return true; + + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + string str = value as string; + + if(str != null) + { + if(str == SR.GetString(SR.Exe, culture)) return OutputType.Exe; + if(str == SR.GetString(SR.Library, culture)) return OutputType.Library; + if(str == SR.GetString(SR.WinExe, culture)) return OutputType.WinExe; + } + + return base.ConvertFrom(context, culture, value); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + if(destinationType == typeof(string)) + { + string result = null; + // In some cases if multiple nodes are selected the windows form engine + // calls us with a null value if the selected node's property values are not equal + if(value != null) + { + result = SR.GetString(((OutputType)value).ToString(), culture); + } + else + { + result = SR.GetString(OutputType.Library.ToString(), culture); + } + + if(result != null) return result; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext context) + { + return true; + } + + public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext context) + { + return new StandardValuesCollection(new OutputType[] { OutputType.Exe, OutputType.Library, OutputType.WinExe }); + } + } + + public class DebugModeConverter : EnumConverter + { + + public DebugModeConverter() + : base(typeof(DebugMode)) + { + + } + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if(sourceType == typeof(string)) return true; + + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + string str = value as string; + + if(str != null) + { + if(str == SR.GetString(SR.Program, culture)) return DebugMode.Program; + + if(str == SR.GetString(SR.Project, culture)) return DebugMode.Project; + + if(str == SR.GetString(SR.URL, culture)) return DebugMode.URL; + } + + return base.ConvertFrom(context, culture, value); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + if(destinationType == typeof(string)) + { + string result = null; + // In some cases if multiple nodes are selected the windows form engine + // calls us with a null value if the selected node's property values are not equal + if(value != null) + { + result = SR.GetString(((DebugMode)value).ToString(), culture); + } + else + { + result = SR.GetString(DebugMode.Program.ToString(), culture); + } + + if(result != null) return result; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext context) + { + return true; + } + + public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext context) + { + return new StandardValuesCollection(new DebugMode[] { DebugMode.Program, DebugMode.Project, DebugMode.URL }); + } + } + + public class BuildActionConverter : EnumConverter + { + + public BuildActionConverter() + : base(typeof(BuildAction)) + { + + } + + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if(sourceType == typeof(string)) return true; + + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + string str = value as string; + + if(str != null) + { + if(str == SR.GetString(SR.Compile, culture)) return BuildAction.Compile; + + if(str == SR.GetString(SR.Content, culture)) return BuildAction.Content; + + if(str == SR.GetString(SR.EmbeddedResource, culture)) return BuildAction.EmbeddedResource; + + if(str == SR.GetString(SR.None, culture)) return BuildAction.None; + } + + return base.ConvertFrom(context, culture, value); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + if(destinationType == typeof(string)) + { + string result = null; + + // In some cases if multiple nodes are selected the windows form engine + // calls us with a null value if the selected node's property values are not equal + // Example of windows form engine passing us null: File set to Compile, Another file set to None, bot nodes are selected, and the build action combo is clicked. + if(value != null) + { + result = SR.GetString(((BuildAction)value).ToString(), culture); + } + else + { + result = SR.GetString(BuildAction.None.ToString(), culture); + } + + if(result != null) return result; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext context) + { + return true; + } + + public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext context) + { + return new StandardValuesCollection(new BuildAction[] { BuildAction.Compile, BuildAction.Content, BuildAction.EmbeddedResource, BuildAction.None }); + } + } + + + + public class PlatformTypeConverter : EnumConverter + { + + public PlatformTypeConverter() + : base(typeof(PlatformType)) + { + } + + public override bool CanConvertFrom(ITypeDescriptorContext context, Type sourceType) + { + if(sourceType == typeof(string)) return true; + + return base.CanConvertFrom(context, sourceType); + } + + public override object ConvertFrom(ITypeDescriptorContext context, CultureInfo culture, object value) + { + string str = value as string; + + if(str != null) + { + if(str == SR.GetString(SR.v1, culture)) return PlatformType.v1; + + if(str == SR.GetString(SR.v11, culture)) return PlatformType.v11; + + if(str == SR.GetString(SR.v2, culture)) return PlatformType.v2; + + if (str == SR.GetString(SR.v3, culture)) return PlatformType.v3; + + if (str == SR.GetString(SR.v35, culture)) return PlatformType.v35; + + if (str == SR.GetString(SR.v4, culture)) return PlatformType.v4; + + if(str == SR.GetString(SR.cli1, culture)) return PlatformType.cli1; + } + + return base.ConvertFrom(context, culture, value); + } + + public override object ConvertTo(ITypeDescriptorContext context, CultureInfo culture, object value, Type destinationType) + { + if(destinationType == typeof(string)) + { + string result = null; + // In some cases if multiple nodes are selected the windows form engine + // calls us with a null value if the selected node's property values are not equal + if(value != null) + { + result = SR.GetString(((PlatformType)value).ToString(), culture); + } + else + { + result = SR.GetString(PlatformType.notSpecified.ToString(), culture); + } + + if(result != null) return result; + } + + return base.ConvertTo(context, culture, value, destinationType); + } + + public override bool GetStandardValuesSupported(System.ComponentModel.ITypeDescriptorContext context) + { + return true; + } + + public override System.ComponentModel.TypeConverter.StandardValuesCollection GetStandardValues(System.ComponentModel.ITypeDescriptorContext context) + { + return new StandardValuesCollection(new PlatformType[] { PlatformType.v1, PlatformType.v11, PlatformType.v2, PlatformType.v3, PlatformType.v35, PlatformType.v4, PlatformType.cli1 }); + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/UIThread.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/UIThread.cs new file mode 100644 index 0000000000..0538c4901a --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/UIThread.cs @@ -0,0 +1,210 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +namespace Microsoft.VisualStudio.Project +{ + using System; + using System.Diagnostics; + using System.Globalization; + using System.Threading; + using System.Windows.Forms; + + internal sealed class UIThread : IDisposable + { + private WindowsFormsSynchronizationContext synchronizationContext; + private bool isUnitTestingMode; + private Thread uithread; +#if DEBUG + /// + /// Stack trace when synchronizationContext was captured + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1823:AvoidUnusedPrivateFields")] + private StackTrace captureStackTrace; +#endif + + /// + /// RunSync puts orignal exception stacktrace to Exception.Data by this key if action throws on UI thread + /// + /// WrappedStacktraceKey is a string to keep exception serializable. + private const string WrappedStacktraceKey = "$$Microsoft.VisualStudio.Package.UIThread.WrappedStacktraceKey$$"; + + /// + /// The singleton instance. + /// + private static volatile UIThread instance = new UIThread(); + + internal UIThread() + { + this.Initialize(); + } + + /// + /// Gets the singleton instance + /// + public static UIThread Instance + { + get + { + return instance; + } + } + + /// + /// Checks whether this is the UI thread. + /// + public bool IsUIThread + { + get { return this.uithread == System.Threading.Thread.CurrentThread; } + } + + #region IDisposable Members + /// + /// Dispose implementation. + /// + public void Dispose() + { + if (this.synchronizationContext != null) + { + this.synchronizationContext.Dispose(); + } + } + + #endregion + + /// + /// Initializes unit testing mode for this object + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal void InitUnitTestingMode() + { + Debug.Assert(this.synchronizationContext == null, "Context has already been captured; too late to InitUnitTestingMode"); + this.isUnitTestingMode = true; + } + + [Conditional("DEBUG")] + internal void MustBeCalledFromUIThread() + { + Debug.Assert(this.uithread == System.Threading.Thread.CurrentThread || this.isUnitTestingMode, "This must be called from the GUI thread"); + } + + /// + /// Runs an action asynchronously on an associated forms synchronization context. + /// + /// The action to run + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + internal void Run(Action a) + { + if (this.isUnitTestingMode) + { + a(); + return; + } + Debug.Assert(this.synchronizationContext != null, "The SynchronizationContext must be captured before calling this method"); +#if DEBUG + StackTrace stackTrace = new StackTrace(true); +#endif + this.synchronizationContext.Post(delegate(object ignore) + { + try + { + this.MustBeCalledFromUIThread(); + a(); + } +#if DEBUG + catch (Exception e) + { + // swallow, random exceptions should not kill process + Debug.Assert(false, string.Format(CultureInfo.InvariantCulture, "UIThread.Run caught and swallowed exception: {0}\n\noriginally invoked from stack:\n{1}", e.ToString(), stackTrace.ToString())); + } +#else + catch (Exception) + { + // swallow, random exceptions should not kill process + } +#endif + }, null); + + } + + /// + /// Runs an action synchronously on an associated forms synchronization context + /// + /// The action to run. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode"), System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1031:DoNotCatchGeneralExceptionTypes")] + internal void RunSync(Action a) + { + if (this.isUnitTestingMode) + { + a(); + return; + } + Exception exn = null; ; + Debug.Assert(this.synchronizationContext != null, "The SynchronizationContext must be captured before calling this method"); + + // Send on UI thread will execute immediately. + this.synchronizationContext.Send(ignore => + { + try + { + this.MustBeCalledFromUIThread(); + a(); + } + catch (Exception e) + { + exn = e; + } + }, null + ); + if (exn != null) + { + // throw exception on calling thread, preserve stacktrace + if (!exn.Data.Contains(WrappedStacktraceKey)) exn.Data[WrappedStacktraceKey] = exn.StackTrace; + throw exn; + } + } + + /// + /// Initializes this object. + /// + private void Initialize() + { + if (this.isUnitTestingMode) return; + this.uithread = System.Threading.Thread.CurrentThread; + + if (this.synchronizationContext == null) + { +#if DEBUG + // This is a handy place to do this, since the product and all interesting unit tests + // must go through this code path. + AppDomain.CurrentDomain.UnhandledException += new UnhandledExceptionEventHandler(delegate(object sender, UnhandledExceptionEventArgs args) + { + if (args.IsTerminating) + { + string s = String.Format(CultureInfo.InvariantCulture, "An unhandled exception is about to terminate the process. Exception info:\n{0}", args.ExceptionObject.ToString()); + Debug.Assert(false, s); + } + }); + + this.captureStackTrace = new StackTrace(true); +#endif + this.synchronizationContext = new WindowsFormsSynchronizationContext(); + } + else + { + // Make sure we are always capturing the same thread. + Debug.Assert(this.uithread == Thread.CurrentThread); + } + } + } +} \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/UpdateSolutionEventsListener.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/UpdateSolutionEventsListener.cs new file mode 100644 index 0000000000..a0a1dd7175 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/UpdateSolutionEventsListener.cs @@ -0,0 +1,285 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; +using IServiceProvider = System.IServiceProvider; +using ShellConstants = Microsoft.VisualStudio.Shell.Interop.Constants; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Defines an abstract class implementing IVsUpdateSolutionEvents interfaces. + /// + [CLSCompliant(false)] + public abstract class UpdateSolutionEventsListener : IVsUpdateSolutionEvents3, IVsUpdateSolutionEvents2, IDisposable + { + #region fields + /// + /// The cookie associated to the the events based IVsUpdateSolutionEvents2. + /// + private uint solutionEvents2Cookie; + + /// + /// The cookie associated to the theIVsUpdateSolutionEvents3 events. + /// + private uint solutionEvents3Cookie; + + /// + /// The IVsSolutionBuildManager2 object controlling the update solution events. + /// + private IVsSolutionBuildManager2 solutionBuildManager; + + + /// + /// The associated service provider. + /// + private IServiceProvider serviceProvider; + + /// + /// Flag determining if the object has been disposed. + /// + private bool isDisposed; + + /// + /// Defines an object that will be a mutex for this object for synchronizing thread calls. + /// + private static volatile object Mutex = new object(); + #endregion + + #region ctors + /// + /// Overloaded constructor. + /// + /// A service provider. + protected UpdateSolutionEventsListener(IServiceProvider serviceProvider) + { + if(serviceProvider == null) + { + throw new ArgumentNullException("serviceProvider"); + } + + this.serviceProvider = serviceProvider; + + this.solutionBuildManager = this.serviceProvider.GetService(typeof(SVsSolutionBuildManager)) as IVsSolutionBuildManager2; + + if(this.solutionBuildManager == null) + { + throw new InvalidOperationException(); + } + + ErrorHandler.ThrowOnFailure(this.solutionBuildManager.AdviseUpdateSolutionEvents(this, out this.solutionEvents2Cookie)); + + Debug.Assert(this.solutionBuildManager is IVsSolutionBuildManager3, "The solution build manager object implementing IVsSolutionBuildManager2 does not implement IVsSolutionBuildManager3"); + ErrorHandler.ThrowOnFailure(this.SolutionBuildManager3.AdviseUpdateSolutionEvents3(this, out this.solutionEvents3Cookie)); + } + #endregion + + #region properties + + /// + /// The associated service provider. + /// + protected IServiceProvider ServiceProvider + { + get + { + return this.serviceProvider; + } + } + + /// + /// The solution build manager object controlling the solution events. + /// + protected IVsSolutionBuildManager2 SolutionBuildManager2 + { + get + { + return this.solutionBuildManager; + } + } + + /// + /// The solution build manager object controlling the solution events. + /// + protected IVsSolutionBuildManager3 SolutionBuildManager3 + { + get + { + return (IVsSolutionBuildManager3)this.solutionBuildManager; + } + + } + #endregion + + #region IVsUpdateSolutionEvents3 Members + + /// + /// Fired after the active solution config is changed (pOldActiveSlnCfg can be NULL). + /// + /// Old configuration. + /// New configuration. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int OnAfterActiveSolutionCfgChange(IVsCfg oldActiveSlnCfg, IVsCfg newActiveSlnCfg) + { + return VSConstants.E_NOTIMPL; + } + + /// + /// Fired before the active solution config is changed (pOldActiveSlnCfg can be NULL + /// + /// Old configuration. + /// New configuration. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int OnBeforeActiveSolutionCfgChange(IVsCfg oldActiveSlnCfg, IVsCfg newActiveSlnCfg) + { + return VSConstants.E_NOTIMPL; + } + + #endregion + + #region IVsUpdateSolutionEvents2 Members + + /// + /// Called when the active project configuration for a project in the solution has changed. + /// + /// The project whose configuration has changed. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int OnActiveProjectCfgChange(IVsHierarchy hierarchy) + { + return VSConstants.E_NOTIMPL; + } + + /// + /// Called right before a project configuration begins to build. + /// + /// The project that is to be build. + /// A configuration project object. + /// A configuration solution object. + /// The action taken. + /// A flag indicating cancel. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + /// The values for the action are defined in the enum _SLNUPDACTION env\msenv\core\slnupd2.h + public int UpdateProjectCfg_Begin(IVsHierarchy hierarchy, IVsCfg configProject, IVsCfg configSolution, uint action, ref int cancel) + { + return VSConstants.E_NOTIMPL; + } + + /// + /// Called right after a project configuration is finished building. + /// + /// The project that has finished building. + /// A configuration project object. + /// A configuration solution object. + /// The action taken. + /// Flag indicating success. + /// Flag indicating cancel. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + /// The values for the action are defined in the enum _SLNUPDACTION env\msenv\core\slnupd2.h + public virtual int UpdateProjectCfg_Done(IVsHierarchy hierarchy, IVsCfg configProject, IVsCfg configSolution, uint action, int success, int cancel) + { + return VSConstants.E_NOTIMPL; + } + + /// + /// Called before any build actions have begun. This is the last chance to cancel the build before any building begins. + /// + /// Flag indicating cancel update. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int UpdateSolution_Begin(ref int cancelUpdate) + { + return VSConstants.E_NOTIMPL; + } + + /// + /// Called when a build is being cancelled. + /// + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int UpdateSolution_Cancel() + { + return VSConstants.E_NOTIMPL; + } + + /// + /// Called when a build is completed. + /// + /// true if no update actions failed. + /// true if any update action succeeded. + /// true if update actions were canceled. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int UpdateSolution_Done(int fSucceeded, int fModified, int fCancelCommand) + { + return VSConstants.E_NOTIMPL; + } + + /// + /// Called before the first project configuration is about to be built. + /// + /// A flag indicating cancel update. + /// If the method succeeds, it returns S_OK. If it fails, it returns an error code. + public virtual int UpdateSolution_StartUpdate(ref int cancelUpdate) + { + return VSConstants.E_NOTIMPL; + } + + #endregion + + + #region IDisposable Members + + /// + /// The IDispose interface Dispose method for disposing the object determinastically. + /// + public void Dispose() + { + this.Dispose(true); + GC.SuppressFinalize(this); + } + + #endregion + + #region methods + /// + /// The method that does the cleanup. + /// + /// true if called from IDispose.Dispose; false if called from Finalizer. + protected virtual void Dispose(bool disposing) + { + // Everybody can go here. + if(!this.isDisposed) + { + // Synchronize calls to the Dispose simultaniously. + lock(Mutex) + { + if(this.solutionEvents2Cookie != (uint)ShellConstants.VSCOOKIE_NIL) + { + ErrorHandler.ThrowOnFailure(this.solutionBuildManager.UnadviseUpdateSolutionEvents(this.solutionEvents2Cookie)); + this.solutionEvents2Cookie = (uint)ShellConstants.VSCOOKIE_NIL; + } + + if(this.solutionEvents3Cookie != (uint)ShellConstants.VSCOOKIE_NIL) + { + ErrorHandler.ThrowOnFailure(this.SolutionBuildManager3.UnadviseUpdateSolutionEvents3(this.solutionEvents3Cookie)); + this.solutionEvents3Cookie = (uint)ShellConstants.VSCOOKIE_NIL; + } + + this.isDisposed = true; + } + } + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Url.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Url.cs new file mode 100644 index 0000000000..0a8e57a04f --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Url.cs @@ -0,0 +1,375 @@ +#if BETA2 +/*************************************************************************** + +Copyright (c) Microsoft Corporation. All rights reserved. +This code is licensed under the Visual Studio SDK license terms. +THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. + +***************************************************************************/ + +using System; +using System.Windows.Forms; +using System.Diagnostics; +using Microsoft.Win32; +using System.Globalization; +using System.IO; +using System.Collections; +using System.Xml; +using System.Text; +using System.Net; +using IOleServiceProvider = Microsoft.VisualStudio.OLE.Interop.IServiceProvider; +using IServiceProvider = System.IServiceProvider; +using ShellConstants = Microsoft.VisualStudio.Shell.Interop.Constants; +using OleConstants = Microsoft.VisualStudio.OLE.Interop.Constants; + +namespace Microsoft.VisualStudio.Package +{ + /// + /// This class wraps the Uri class and provides an unescaped "LocalPath" for file URL's + /// and an unescaped AbsoluteUri for other schemes, plus it also returned an un-hex-escaped + /// result from MakeRelative so it can be presented to the user. + /// + public class Url + { + private Uri uri = null; + private bool isFile; + + + public Url(string path) + { + Init(path); + } + + void Init(string path) + { + // Must try absolute first, then fall back on relative, otherwise it + // makes some absolute UNC paths like (\\lingw11\Web_test\) relative! + if (path != null) + { + + if (!Uri.TryCreate(path, UriKind.Absolute, out this.uri)) + { + Uri.TryCreate(path, UriKind.Relative, out this.uri); + } + + this.CheckIsFile(); + } + } + + void CheckIsFile() + { + this.isFile = false; + if (this.uri != null) + { + if (this.uri.IsAbsoluteUri) + { + this.isFile = this.uri.IsFile; + } + else + { + string[] test1 = this.uri.OriginalString.Split('/'); + string[] test2 = this.uri.OriginalString.Split('\\'); + if (test1.Length < test2.Length) + { + this.isFile = true; + } + } + } + } + + // allows relpath to be null, in which case it just returns the baseUrl. + + public Url(Url baseUrl, string relpath) + { + if (baseUrl.uri == null) + { + Init(relpath); + } + else if (string.IsNullOrEmpty(relpath)) + { + this.uri = baseUrl.uri; + } + else + { + this.uri = new Uri(baseUrl.uri, relpath); + } + CheckIsFile(); + } + + + public string AbsoluteUrl + { + get + { + if (this.uri == null) return null; + if (this.uri.IsAbsoluteUri) + { + if (this.isFile) + { + // Fix for build break. UriComponents.LocalPath is no longer available. + // return uri.GetComponents(UriComponents.LocalPath, UriFormat.SafeUnescaped); + return uri.LocalPath; + } + else + { + return uri.GetComponents(UriComponents.AbsoluteUri, UriFormat.SafeUnescaped); + } + } + else + { + return uri.OriginalString; + } + } + } + + + /// Returns the AbsoluteUrl for the parent directory containing the file + /// referenced by this URL object, where the Directory string is also unescaped. + public string Directory + { + get + { + string path = this.AbsoluteUrl; + if (path == null) return null; + int i = path.LastIndexOf(this.IsFile ? Path.DirectorySeparatorChar : '/'); + int len = (i > 0) ? i : path.Length; + return path.Substring(0, len); + } + } + + + public bool IsFile + { + get { return this.isFile; } + } + + + public Url Move(Url oldBase, Url newBase) + { + if (this.uri == null || oldBase.uri == null) return null; + string rel = oldBase.uri.MakeRelativeUri(this.uri).ToString(); + return new Url(newBase, rel); + } + + // return an un-escaped relative path + + public string MakeRelative(Url url) + { + if (this.uri == null || url.uri == null) return null; + if (this.uri.Scheme != url.uri.Scheme || this.uri.Host != url.uri.Host) + { + // Then it cannot be relatavized (e.g from file:// to http://). + return url.AbsoluteUrl; + } + // This will return a hex-escaped string. + string rel = this.uri.MakeRelativeUri(url.uri).ToString(); + + // So unescape it. + return Unescape(rel, this.isFile); + } + + const char c_DummyChar = (char)0xFFFF; + + private static char EscapedAscii(char digit, char next) + { + // Only accept hexadecimal characters + if (!(((digit >= '0') && (digit <= '9')) + || ((digit >= 'A') && (digit <= 'F')) + || ((digit >= 'a') && (digit <= 'f')))) + { + return c_DummyChar; + } + + int res = 0; + if (digit <= '9') + res = (int)digit - (int)'0'; + else if (digit <= 'F') + res = ((int)digit - (int)'A') + 10; + else + res = ((int)digit - (int)'a') + 10; + + // Only accept hexadecimal characters + if (!(((next >= '0') && (next <= '9')) + || ((next >= 'A') && (next <= 'F')) + || ((next >= 'a') && (next <= 'f')))) + { + return c_DummyChar; + } + + res = res << 4; + if (next <= '9') + res += (int)next - (int)'0'; + else if (digit <= 'F') + res += ((int)next - (int)'A') + 10; + else + res += ((int)next - (int)'a') + 10; + + return (char)(res); + } + + + public static string Unescape(string escaped, bool isFile) + { + if (String.IsNullOrEmpty(escaped)) + { + return String.Empty; + } + + byte[] bytes = null; + char[] dest = new char[escaped.Length]; + int j = 0; + + for (int i = 0, end = escaped.Length; i < end; i++) + { + char ch = escaped[i]; + if (ch != '%') + { + if (ch == '/' && isFile) + { + ch = Path.DirectorySeparatorChar; + } + dest[j++] = ch; + } + else + { + int byteCount = 0; + // lazy initialization of max size, will reuse the array for next sequences + if (bytes == null) + { + bytes = new byte[end - i]; + } + + do + { + // Check on exit criterion + if ((ch = escaped[i]) != '%' || (end - i) < 3) + { + break; + } + // already made sure we have 3 characters in str + ch = EscapedAscii(escaped[i + 1], escaped[i + 2]); + if (ch == c_DummyChar) + { + //invalid hex sequence, we will out '%' character + ch = '%'; + break; + } + else if (ch < '\x80') + { + // character is not part of a UTF-8 sequence + i += 2; + break; + } + else + { + //a UTF-8 sequence + bytes[byteCount++] = (byte)ch; + i += 3; + } + } while (i < end); + + if (byteCount != 0) + { + + int charCount = Encoding.UTF8.GetCharCount(bytes, 0, byteCount); + if (charCount != 0) + { + Encoding.UTF8.GetChars(bytes, 0, byteCount, dest, j); + j += charCount; + } + else + { + // the encoded, high-ANSI characters are not UTF-8 encoded + for (int k = 0; k < byteCount; ++k) + { + dest[j++] = (char)bytes[k]; + } + } + } + if (i < end) + { + dest[j++] = ch; + } + } + } + return new string(dest, 0, j); + } + + + public Uri Uri + { + get { return this.uri; } + } + + // + // Unlike the Uri class, this ALWAYS succeeds, even on relative paths, and it + // strips out the path separator characters + + public string[] GetSegments() + { + if (this.uri == null) return null; + string path = this.AbsoluteUrl; + if (this.isFile || !this.uri.IsAbsoluteUri) + { + if (path.EndsWith("\\")) + path = path.Substring(0, path.Length - 1); + return path.Split(Path.DirectorySeparatorChar); + } + else + { + // strip off "http://" and host name, since those are not part of the path. + path = path.Substring(this.uri.Scheme.Length + 3 + this.uri.Host.Length + 1); + if (path.EndsWith("/")) + path = path.Substring(0, path.Length - 1); + return path.Split('/'); + } + } + + + /// Return unescaped path up to (but not including) segment i. + public string GetPartial(int i) + { + string path = JoinSegments(0, i); + if (!this.isFile) + { + // prepend "http://host/" + path = this.uri.Scheme + "://" + this.uri.Host + '/' + path; + } + return path; + } + + + /// Return unescaped relative path starting segment i. + public string GetRemainder(int i) + { + return JoinSegments(i, -1); + } + + + public string JoinSegments(int i, int j) + { + if (i < 0) + throw new ArgumentOutOfRangeException("i"); + + StringBuilder sb = new StringBuilder(); + string[] segments = this.GetSegments(); + if (segments == null) + return null; + if (j < 0) + j = segments.Length; + int len = segments.Length; + for (; i < j && i < len; i++) + { + if (sb.Length > 0) + sb.Append(this.isFile ? Path.DirectorySeparatorChar : '/'); + string s = segments[i]; + sb.Append(s); + } + return Unescape(sb.ToString(), isFile); + } + } +} +#endif \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/UserProjectSecurityChecker.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/UserProjectSecurityChecker.cs new file mode 100644 index 0000000000..71f4737338 --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/UserProjectSecurityChecker.cs @@ -0,0 +1,189 @@ +/*************************************************************************** + +Copyright (c) Microsoft Corporation. All rights reserved. +This code is licensed under the Visual Studio SDK license terms. +THIS CODE IS PROVIDED *AS IS* WITHOUT WARRANTY OF +ANY KIND, EITHER EXPRESS OR IMPLIED, INCLUDING ANY +IMPLIED WARRANTIES OF FITNESS FOR A PARTICULAR +PURPOSE, MERCHANTABILITY, OR NON-INFRINGEMENT. + +***************************************************************************/ + +using System; +using System.Globalization; +using System.IO; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Does security validation of a user project before loading the project. + /// + public class UserProjectSecurityChecker : ProjectSecurityChecker + { + #region fields + /// + /// The project shim for the main project file. + /// We need this otherwise the msbuild API cannot check the user file. + /// + private ProjectShim mainProjectShim; + + #endregion + + #region ctors + /// + /// Overloaded Constructor + /// + /// path to the project file + /// A service provider. + public UserProjectSecurityChecker(IServiceProvider serviceProvider, string projectFilePath) : + base(serviceProvider, projectFilePath) + { + } + #endregion + + #region properties + /// + /// The main projects' shim. + /// + internal protected ProjectShim MainProjectShim + { + get + { + return this.mainProjectShim; + } + internal set + { + this.mainProjectShim = value; + } + } + #endregion + + #region overridden method + /// + /// Checks if the user file is safe with imports. If it has then the user file is considered unsafe. + /// + /// At return describes the reason why the projects is not considered safe. + /// true if the user project is safe regarding imports. + protected override bool IsProjectSafeWithImports(out string securityErrorMessage) + { + securityErrorMessage = String.Empty; + + string[] directImports = this.SecurityCheckHelper.GetDirectlyImportedProjects(this.ProjectShim); + + if(directImports != null && directImports.Length > 0) + { + securityErrorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.DetailsUserImport, CultureInfo.CurrentUICulture), Path.GetFileName(this.ProjectShim.FullFileName), directImports[0]); + return false; + } + + return true; + } + + /// + /// Checks if the project is safe regarding properties. + /// + /// At return describes the reason why the projects is not considered safe. + /// true if the project has only safe properties. + protected override bool IsProjectSafeWithProperties(out string securityErrorMessage) + { + securityErrorMessage = String.Empty; + + // Now ask the security check heper for the safe properties. + string reasonForFailure; + bool isUserFile; + bool isProjectSafe = this.SecurityCheckHelper.IsProjectSafe(ProjectSecurityChecker.DangerousPropertyProperty, + ProjectSecurityChecker.DefaultDangerousProperties, + this.mainProjectShim, + this.ProjectShim, + SecurityCheckPass.Properties, + out reasonForFailure, + out isUserFile); + + // Main project gets precedence over the user project. + // Do not report that since this is only for the user file. + if(!isUserFile) + { + return true; + } + + if(!isProjectSafe) + { + securityErrorMessage = this.GetMessageString(reasonForFailure, SR.DetailsProperty); + } + + return isProjectSafe; + } + + /// + /// Checks if the project is safe regarding targets. + /// + /// At return describes the reason why the projects is not considered safe. + /// true if the project has only safe targets. + protected override bool IsProjectSafeWithTargets(out string securityErrorMessage) + { + securityErrorMessage = String.Empty; + + // Now ask the security check heper for the safe targets. + string reasonForFailure; + bool isUserFile; + bool isProjectSafe = this.SecurityCheckHelper.IsProjectSafe(ProjectSecurityChecker.DangerousTargetProperty, + ProjectSecurityChecker.DefaultDangerousTargets, + this.mainProjectShim, + this.ProjectShim, + SecurityCheckPass.Targets, + out reasonForFailure, + out isUserFile); + + // Main project gets precedence over the user project. + // Do not report that since this is only for the user file. + if(!isUserFile) + { + return true; + } + + if(!isProjectSafe) + { + securityErrorMessage = this.GetMessageString(reasonForFailure, SR.DetailsTarget); + } + + return isProjectSafe; + } + + /// + /// Checks if the project is safe regarding items. + /// + /// At return describes the reason why the projects is not considered safe. + /// true if the project has only safe items. + protected override bool IsProjectSafeWithItems(out string securityErrorMessage) + { + securityErrorMessage = String.Empty; + + // Now ask the security check heper for the safe items. + string reasonForFailure; + bool isUserFile; + + bool isProjectSafe = this.SecurityCheckHelper.IsProjectSafe(ProjectSecurityChecker.DangerousItemsProperty, + ProjectSecurityChecker.DefaultDangerousItems, + this.mainProjectShim, + this.ProjectShim, + SecurityCheckPass.Items, + out reasonForFailure, + out isUserFile); + + // Main project gets precedence over the user project. + // Do not report that since this is only for the user file. + if(!isUserFile) + { + return true; + } + + if(!isProjectSafe) + { + securityErrorMessage = this.GetMessageString(reasonForFailure, SR.DetailsItem); + } + + return isProjectSafe; + } + #endregion + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/Utilities.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/Utilities.cs new file mode 100644 index 0000000000..c6985a791a --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/Utilities.cs @@ -0,0 +1,995 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + + +using System; +using System.Collections.Generic; +using System.ComponentModel; +using System.Diagnostics; +using System.Diagnostics.CodeAnalysis; +using System.Drawing; +using System.Globalization; +using System.IO; +using System.Runtime.InteropServices; +using System.Security.Permissions; +using System.Security.Policy; +using System.Text; +using System.Text.RegularExpressions; +using System.Windows.Forms; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.OLE.Interop; +using Microsoft.VisualStudio.Shell; +using Microsoft.VisualStudio.Shell.Interop; +using Microsoft.Win32; +using IServiceProvider = System.IServiceProvider; +using MSBuild = Microsoft.Build.Evaluation; +using VSRegistry = Microsoft.VisualStudio.Shell.VSRegistry; + +namespace Microsoft.VisualStudio.Project +{ + public static class Utilities + { + private const string defaultMSBuildVersion = "4.0"; + + /// + /// Look in the registry under the current hive for the path + /// of MSBuild + /// + /// + [CLSCompliant(false)] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "msbuild")] + public static string GetMsBuildPath(IServiceProvider serviceProvider) + { + return GetMsBuildPath(serviceProvider, defaultMSBuildVersion); + } + + /// + /// Search the registry for the tools path for MSBuild. + /// + /// The service provider. + /// Msbuild version. + /// The msbuild tools path + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Ms")] + public static string GetMsBuildPath(IServiceProvider serviceProvider, string version) + { + string msBuildPath = null; + using(RegistryKey root = VSRegistry.RegistryRoot(serviceProvider, __VsLocalRegistryType.RegType_Configuration, false)) + { + // Get the value from the registry + using(RegistryKey vsKey = root.OpenSubKey("MSBuild", false)) + { + msBuildPath = (string)vsKey.GetValue("MSBuildBinPath", null); + } + } + if(!string.IsNullOrEmpty(msBuildPath)) + { + return msBuildPath; + } + + // The path to MSBuild was not found in the VisualStudio's registry hive, so try to + // find it in the new MSBuild hive. + string registryPath = string.Format(CultureInfo.InvariantCulture, "Software\\Microsoft\\MSBuild\\ToolsVersions\\{0}", version); + using(Microsoft.Win32.RegistryKey msbuildKey = Microsoft.Win32.Registry.LocalMachine.OpenSubKey(registryPath, false)) + { + msBuildPath = (string)msbuildKey.GetValue("MSBuildToolsPath", null); + } + if(string.IsNullOrEmpty(msBuildPath)) + { + string error = SR.GetString(SR.ErrorMsBuildRegistration, CultureInfo.CurrentUICulture); + throw new FileLoadException(error); + } + return msBuildPath; + } + + /// + /// Is Visual Studio in design mode. + /// + /// The service provider. + /// true if visual studio is in design mode + public static bool IsVisualStudioInDesignMode(IServiceProvider site) + { + if (site == null) + { + throw new ArgumentNullException("site"); + } + + IVsMonitorSelection selectionMonitor = site.GetService(typeof(IVsMonitorSelection)) as IVsMonitorSelection; + uint cookie = 0; + int active = 0; + Guid designContext = VSConstants.UICONTEXT_DesignMode; + ErrorHandler.ThrowOnFailure(selectionMonitor.GetCmdUIContextCookie(ref designContext, out cookie)); + ErrorHandler.ThrowOnFailure(selectionMonitor.IsCmdUIContextActive(cookie, out active)); + return active != 0; + } + + /// + /// + /// Is an extensibility object executing an automation function. + /// + /// The service provider. + /// true if the extensiblity object is executing an automation function. + public static bool IsInAutomationFunction(IServiceProvider serviceProvider) + { + if(serviceProvider == null) + { + throw new ArgumentNullException("serviceProvider"); + } + + IVsExtensibility3 extensibility = serviceProvider.GetService(typeof(EnvDTE.IVsExtensibility)) as IVsExtensibility3; + + if(extensibility == null) + { + throw new InvalidOperationException(); + } + int inAutomation = 0; + ErrorHandler.ThrowOnFailure(extensibility.IsInAutomationFunction(out inAutomation)); + return inAutomation != 0; + } + + /// + /// Creates a semicolon delinited list of strings. This can be used to provide the properties for VSHPROPID_CfgPropertyPagesCLSIDList, VSHPROPID_PropertyPagesCLSIDList, VSHPROPID_PriorityPropertyPagesCLSIDList + /// + /// An array of Guids. + /// A semicolon delimited string, or null + [CLSCompliant(false)] + public static string CreateSemicolonDelimitedListOfStringFromGuids(Guid[] guids) + { + if(guids == null || guids.Length == 0) + { + return null; + } + + // Create a StringBuilder with a pre-allocated buffer big enough for the + // final string. 39 is the length of a GUID in the "B" form plus the final ';' + StringBuilder stringList = new StringBuilder(39 * guids.Length); + for(int i = 0; i < guids.Length; i++) + { + stringList.Append(guids[i].ToString("B")); + stringList.Append(";"); + } + + return stringList.ToString().TrimEnd(';'); + } + + private static char[] curlyBraces = new char[] { '{', '}' }; + /// + /// Take list of guids as a single string and generate an array of Guids from it + /// + /// Semi-colon separated list of Guids + /// Array of Guids + [CLSCompliant(false)] + public static Guid[] GuidsArrayFromSemicolonDelimitedStringOfGuids(string guidList) + { + if(guidList == null) + { + return null; + } + + List guids = new List(); + string[] guidsStrings = guidList.Split(';'); + foreach(string guid in guidsStrings) + { + if(!String.IsNullOrEmpty(guid)) + guids.Add(new Guid(guid.Trim(curlyBraces))); + } + + return guids.ToArray(); + } + + /// + /// Validates a file path by validating all file parts. If the + /// the file name is invalid it throws an exception if the project is in automation. Otherwise it shows a dialog box with the error message. + /// + /// The service provider + /// A full path to a file name + /// In case of failure an InvalidOperationException is thrown. + public static void ValidateFileName(IServiceProvider serviceProvider, string filePath) + { + string errorMessage = String.Empty; + if(String.IsNullOrEmpty(filePath)) + { + errorMessage = SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture); + } + else if(filePath.Length > NativeMethods.MAX_PATH) + { + errorMessage = String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.PathTooLong, CultureInfo.CurrentUICulture), filePath); + } + else if(ContainsInvalidFileNameChars(filePath)) + { + errorMessage = SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture); + } + + if(errorMessage.Length == 0) + { + string fileName = Path.GetFileName(filePath); + if(String.IsNullOrEmpty(fileName) || IsFileNameInvalid(fileName)) + { + errorMessage = SR.GetString(SR.ErrorInvalidFileName, CultureInfo.CurrentUICulture); + } + else + { + string fileNameWithoutExtension = Path.GetFileNameWithoutExtension(fileName); + + // If there is no filename or it starts with a leading dot issue an error message and quit. + if(String.IsNullOrEmpty(fileNameWithoutExtension) || fileNameWithoutExtension[0] == '.') + { + errorMessage = SR.GetString(SR.FileNameCannotContainALeadingPeriod, CultureInfo.CurrentUICulture); + } + } + } + + if(errorMessage.Length > 0) + { + // If it is not called from an automation method show a dialog box. + if(!Utilities.IsInAutomationFunction(serviceProvider)) + { + string title = null; + OLEMSGICON icon = OLEMSGICON.OLEMSGICON_CRITICAL; + OLEMSGBUTTON buttons = OLEMSGBUTTON.OLEMSGBUTTON_OK; + OLEMSGDEFBUTTON defaultButton = OLEMSGDEFBUTTON.OLEMSGDEFBUTTON_FIRST; + VsShellUtilities.ShowMessageBox(serviceProvider, title, errorMessage, icon, buttons, defaultButton); + } + else + { + throw new InvalidOperationException(errorMessage); + } + } + + } + + /// + /// Creates a CALPOLESTR from a list of strings + /// It is the responsability of the caller to release this memory. + /// + /// + /// A CALPOLESTR that was created from the the list of strings. + [CLSCompliant(false)] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CALPOLESTR")] + public static CALPOLESTR CreateCALPOLESTR(IList strings) + { + CALPOLESTR calpolStr = new CALPOLESTR(); + + if(strings != null) + { + // Demand unmanaged permissions in order to access unmanaged memory. + new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); + + calpolStr.cElems = (uint)strings.Count; + + int size = Marshal.SizeOf(typeof(IntPtr)); + + calpolStr.pElems = Marshal.AllocCoTaskMem(strings.Count * size); + + IntPtr ptr = calpolStr.pElems; + + foreach(string aString in strings) + { + IntPtr tempPtr = Marshal.StringToCoTaskMemUni(aString); + Marshal.WriteIntPtr(ptr, tempPtr); + ptr = new IntPtr(ptr.ToInt64() + size); + } + } + + return calpolStr; + } + + /// + /// Creates a CADWORD from a list of tagVsSccFilesFlags. Memory is allocated for the elems. + /// It is the responsability of the caller to release this memory. + /// + /// + /// A CADWORD created from the list of tagVsSccFilesFlags. + [CLSCompliant(false)] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CADWORD")] + public static CADWORD CreateCADWORD(IList flags) + { + CADWORD cadWord = new CADWORD(); + + if(flags != null) + { + // Demand unmanaged permissions in order to access unmanaged memory. + new SecurityPermission(SecurityPermissionFlag.UnmanagedCode).Demand(); + + cadWord.cElems = (uint)flags.Count; + + int size = Marshal.SizeOf(typeof(UInt32)); + + cadWord.pElems = Marshal.AllocCoTaskMem(flags.Count * size); + + IntPtr ptr = cadWord.pElems; + + foreach(tagVsSccFilesFlags flag in flags) + { + Marshal.WriteInt32(ptr, (int)flag); + ptr = new IntPtr(ptr.ToInt64() + size); + } + } + + return cadWord; + } + + /// + /// Splits a bitmap from a Stream into an ImageList + /// + /// A Stream representing a Bitmap + /// An ImageList object representing the images from the given stream + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] + public static ImageList GetImageList(Stream imageStream) + { + ImageList ilist = new ImageList(); + + if(imageStream == null) + { + return ilist; + } + ilist.ColorDepth = ColorDepth.Depth24Bit; + ilist.ImageSize = new Size(16, 16); + Bitmap bitmap = new Bitmap(imageStream); + ilist.Images.AddStrip(bitmap); + ilist.TransparentColor = Color.Magenta; + return ilist; + } + + /// + /// Splits a bitmap from a pointer to an ImageList + /// + /// A pointer to a bitmap of images to split + /// An ImageList object representing the images from the given stream + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Reliability", "CA2000:Dispose objects before losing scope")] + public static ImageList GetImageList(object imageListAsPointer) + { + ImageList images = null; + + IntPtr intPtr = new IntPtr((int)imageListAsPointer); + HandleRef hImageList = new HandleRef(null, intPtr); + int count = UnsafeNativeMethods.ImageList_GetImageCount(hImageList); + + if(count > 0) + { + // Create a bitmap big enough to hold all the images + Bitmap b = new Bitmap(16 * count, 16); + Graphics g = Graphics.FromImage(b); + + // Loop through and extract each image from the imagelist into our own bitmap + IntPtr hDC = IntPtr.Zero; + try + { + hDC = g.GetHdc(); + HandleRef handleRefDC = new HandleRef(null, hDC); + for(int i = 0; i < count; i++) + { + UnsafeNativeMethods.ImageList_Draw(hImageList, i, handleRefDC, i * 16, 0, NativeMethods.ILD_NORMAL); + } + } + finally + { + if(g != null && hDC != IntPtr.Zero) + { + g.ReleaseHdc(hDC); + } + } + + // Create a new imagelist based on our stolen images + images = new ImageList(); + images.ColorDepth = ColorDepth.Depth24Bit; + images.ImageSize = new Size(16, 16); + images.Images.AddStrip(b); + } + return images; + } + + /// + /// Gets the active configuration name. + /// + /// The automation object. + /// The name of the active configuartion. + internal static string GetActiveConfigurationName(EnvDTE.Project automationObject) + { + if(automationObject == null) + { + throw new ArgumentNullException("automationObject"); + } + + string currentConfigName = string.Empty; + if(automationObject.ConfigurationManager != null) + { + EnvDTE.Configuration activeConfig = automationObject.ConfigurationManager.ActiveConfiguration; + if(activeConfig != null) + { + currentConfigName = activeConfig.ConfigurationName; + } + } + return currentConfigName; + + } + + + /// + /// Verifies that two objects represent the same instance of a COM object. + /// This essentially compares the IUnkown pointers of the 2 objects. + /// This is needed in scenario where aggregation is involved. + /// + /// Can be an object, interface or IntPtr + /// Can be an object, interface or IntPtr + /// True if the 2 items represent the same thing + [SuppressMessage("Microsoft.Naming", "CA1720:IdentifiersShouldNotContainTypeNames", MessageId = "obj")] + public static bool IsSameComObject(object obj1, object obj2) + { + bool isSame = false; + IntPtr unknown1 = IntPtr.Zero; + IntPtr unknown2 = IntPtr.Zero; + try + { + // If we have 2 null, then they are not COM objects and as such "it's not the same COM object" + if(obj1 != null && obj2 != null) + { + unknown1 = QueryInterfaceIUnknown(obj1); + unknown2 = QueryInterfaceIUnknown(obj2); + + isSame = IntPtr.Equals(unknown1, unknown2); + } + } + finally + { + if(unknown1 != IntPtr.Zero) + { + Marshal.Release(unknown1); + } + + if(unknown2 != IntPtr.Zero) + { + Marshal.Release(unknown2); + } + + } + + return isSame; + } + + /// + /// Retrieve the IUnknown for the managed or COM object passed in. + /// + /// Managed or COM object. + /// Pointer to the IUnknown interface of the object. + internal static IntPtr QueryInterfaceIUnknown(object objToQuery) + { + bool releaseIt = false; + IntPtr unknown = IntPtr.Zero; + IntPtr result; + try + { + if(objToQuery is IntPtr) + { + unknown = (IntPtr)objToQuery; + } + else + { + // This is a managed object (or RCW) + unknown = Marshal.GetIUnknownForObject(objToQuery); + releaseIt = true; + } + + // We might already have an IUnknown, but if this is an aggregated + // object, it may not be THE IUnknown until we QI for it. + Guid IID_IUnknown = VSConstants.IID_IUnknown; + ErrorHandler.ThrowOnFailure(Marshal.QueryInterface(unknown, ref IID_IUnknown, out result)); + } + finally + { + if(releaseIt && unknown != IntPtr.Zero) + { + Marshal.Release(unknown); + } + + } + + return result; + } + + /// + /// Returns true if thename that can represent a path, absolut or relative, or a file name contains invalid filename characters. + /// + /// File name + /// true if file name is invalid + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1062:Validate arguments of public methods", MessageId = "0", + Justification="The name is validated.")] + public static bool ContainsInvalidFileNameChars(string name) + { + if(String.IsNullOrEmpty(name)) + { + return true; + } + + try + { + if(Path.IsPathRooted(name) && !name.StartsWith(@"\\", StringComparison.Ordinal)) + { + string root = Path.GetPathRoot(name); + name = name.Substring(root.Length); + } + } + // The Path methods used by ContainsInvalidFileNameChars return argument exception if the filePath contains invalid characters. + catch(ArgumentException) + { + return true; + } + + Microsoft.VisualStudio.Shell.Url uri = new Microsoft.VisualStudio.Shell.Url(name); + + // This might be confusing bur Url.IsFile means that the uri represented by the name is either absolut or relative. + if(uri.IsFile) + { + string[] segments = uri.Segments; + if(segments != null && segments.Length > 0) + { + foreach(string segment in segments) + { + if(IsFilePartInValid(segment)) + { + return true; + } + } + + // Now the last segment should be specially taken care, since that cannot be all dots or spaces. + string lastSegment = segments[segments.Length - 1]; + string filePart = Path.GetFileNameWithoutExtension(lastSegment); + if(IsFileNameAllGivenCharacter('.', filePart) || IsFileNameAllGivenCharacter(' ', filePart)) + { + return true; + } + } + } + else + { + // The assumption here is that we got a file name. + string filePart = Path.GetFileNameWithoutExtension(name); + if(IsFileNameAllGivenCharacter('.', filePart) || IsFileNameAllGivenCharacter(' ', filePart)) + { + return true; + } + + + return IsFilePartInValid(name); + } + + return false; + } + + /// Cehcks if a file name is valid. + /// + /// The name of the file + /// True if the file is valid. + public static bool IsFileNameInvalid(string fileName) + { + if(String.IsNullOrEmpty(fileName)) + { + return true; + } + + if(IsFileNameAllGivenCharacter('.', fileName) || IsFileNameAllGivenCharacter(' ', fileName)) + { + return true; + } + + + return IsFilePartInValid(fileName); + + } + + /// + /// Helper method to call a converter explicitely to convert to an enum type + /// + /// THe enum to convert to + /// The converter that will be created + /// The enum value to be converted to + /// The type to convert + /// The culture to use to read the localized strings + /// + [CLSCompliant(false)] + public static object ConvertToType(T value, Type typeToConvert, CultureInfo culture) + where T : struct + { + EnumConverter converter = GetEnumConverter(); + if(converter == null) + { + return null; + } + if(converter.CanConvertTo(typeToConvert)) + { + return converter.ConvertTo(null, culture, value, typeToConvert); + } + + return null; + } + + /// + /// Helper method for converting from a string to an enum using a converter. + /// + /// + /// + /// The culture to use to read the localized strings + /// + [CLSCompliant(false)] + public static Nullable ConvertFromType(string value, CultureInfo culture) + where T : struct + { + Nullable returnValue = new Nullable(); + + returnValue = returnValue.GetValueOrDefault(); + + if(value == null) + { + return returnValue; + } + + EnumConverter converter = GetEnumConverter(); + if(converter == null) + { + return returnValue; + } + + if(converter.CanConvertFrom(value.GetType())) + { + object converted = converter.ConvertFrom(null, culture, value); + + if(converted != null && (converted is T)) + { + returnValue = (T)converted; + } + } + + return returnValue; + } + + + /// + /// Sets a string value from an enum + /// + /// The enum type + /// The value of teh enum. + /// + [CLSCompliant(false)] + public static string SetStringValueFromConvertedEnum(T enumValue, CultureInfo culture) + where T : struct + { + string convertToType = ConvertToType(enumValue, typeof(string), culture) as string; + + if(convertToType == null) + { + return String.Empty; + } + + return convertToType; + } + + + /// + /// Initializes the in memory project. Sets BuildEnabled on the project to true. + /// + /// The build engine to use to create a build project. + /// The full path of the project. + /// A loaded msbuild project. + internal static MSBuild.Project InitializeMsBuildProject(MSBuild.ProjectCollection buildEngine, string fullProjectPath) + { + if(String.IsNullOrEmpty(fullProjectPath)) + { + throw new ArgumentException(SR.GetString(SR.InvalidParameter, CultureInfo.CurrentUICulture), "fullProjectPath"); + } + + // Call GetFullPath to expand any relative path passed into this method. + fullProjectPath = Path.GetFullPath(fullProjectPath); + + + // Check if the project already has been loaded with the fullProjectPath. If yes return the build project associated to it. + List loadedProject = new List(buildEngine.GetLoadedProjects(fullProjectPath)); + MSBuild.Project buildProject = loadedProject != null && loadedProject.Count > 0 && loadedProject[0] != null ? loadedProject[0] : null; + + if(buildProject == null) + { + buildProject = buildEngine.LoadProject(fullProjectPath); + } + + return buildProject; + } + + /// + /// Loads a project file for the file. If the build project exists and it was loaded with a different file then it is unloaded first. + /// + /// The build engine to use to create a build project. + /// The full path of the project. + /// An Existing build project that will be reloaded. + /// A loaded msbuild project. + internal static MSBuild.Project ReinitializeMsBuildProject(MSBuild.ProjectCollection buildEngine, string fullProjectPath, MSBuild.Project exitingBuildProject) + { + // If we have a build project that has been loaded with another file unload it. + try + { + if(exitingBuildProject != null && exitingBuildProject.ProjectCollection != null && !NativeMethods.IsSamePath(exitingBuildProject.FullPath, fullProjectPath)) + { + buildEngine.UnloadProject(exitingBuildProject); + } + } + // We catch Invalid operation exception because if the project was unloaded while we touch the ParentEngine the msbuild API throws. + // Is there a way to figure out that a project was unloaded? + catch(InvalidOperationException) + { + } + + return Utilities.InitializeMsBuildProject(buildEngine, fullProjectPath); + } + + /// + /// Initialize the build engine. Sets the build enabled property to true. The engine is initialzed if the passed in engine is null or does not have its bin path set. + /// + /// An instance of MSBuild.ProjectCollection build engine, that will be checked if initialized. + /// The service provider. + /// The buildengine to use. + internal static MSBuild.ProjectCollection InitializeMsBuildEngine(MSBuild.ProjectCollection existingEngine, IServiceProvider serviceProvider) + { + if(serviceProvider == null) + { + throw new ArgumentNullException("serviceProvider"); + } + + if(existingEngine == null) + { + MSBuild.ProjectCollection buildEngine = MSBuild.ProjectCollection.GlobalProjectCollection; + return buildEngine; + } + + return existingEngine; + } + + /// + /// Gets an instance of an EnumConverter for enums that have PropertyPageTypeConverter attribute + /// + /// The type to search for the PropertyPageTypeConverter attribute. + /// An instance of an enum converter, or null if none found. + private static EnumConverter GetEnumConverter() + where T : struct + { + object[] attributes = typeof(T).GetCustomAttributes(typeof(PropertyPageTypeConverterAttribute), true); + + // There should be only one PropertyPageTypeConverterAttribute defined on T + if(attributes != null && attributes.Length == 1) + { + + Debug.Assert(attributes[0] is PropertyPageTypeConverterAttribute, "The returned attribute must be an attribute is PropertyPageTypeConverterAttribute"); + PropertyPageTypeConverterAttribute converterAttribute = (PropertyPageTypeConverterAttribute)attributes[0]; + + if(converterAttribute.ConverterType.IsSubclassOf(typeof(EnumConverter))) + { + return Activator.CreateInstance(converterAttribute.ConverterType) as EnumConverter; + } + } + + return null; + } + + /// > + /// Checks if the file name is all the given character. + /// + private static bool IsFileNameAllGivenCharacter(char c, string fileName) + { + // A valid file name cannot be all "c" . + int charFound = 0; + for(charFound = 0; charFound < fileName.Length && fileName[charFound] == c; ++charFound) ; + if(charFound >= fileName.Length) + { + return true; + } + + return false; + } + + /// + /// Checks whether a file part contains valid characters. The file part can be any part of a non rooted path. + /// + /// + /// + private static bool IsFilePartInValid(string filePart) + { + if(String.IsNullOrEmpty(filePart)) + { + return true; + } + String reservedName = "(\\b(nul|con|aux|prn)\\b)|(\\b((com|lpt)[0-9])\\b)"; + String invalidChars = @"([/?:&\\*<>|#%" + '\"' + "])"; + String regexToUseForFileName = reservedName + "|" + invalidChars; + String fileNameToVerify = filePart; + + // Define a regular expression that covers all characters that are not in the safe character sets. + // It is compiled for performance. + + // The filePart might still be a file and extension. If it is like that then we must check them separately, since different rules apply + string extension = String.Empty; + try + { + extension = Path.GetExtension(filePart); + } + // We catch the ArgumentException because we want this method to return true if the filename is not valid. FilePart could be for example #¤&%"¤&"% and that would throw ArgumentException on GetExtension + catch(ArgumentException) + { + return true; + } + + if(!String.IsNullOrEmpty(extension)) + { + // Check the extension first + String regexToUseForExtension = invalidChars; + Regex unsafeCharactersRegex = new Regex(regexToUseForExtension, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + bool isMatch = unsafeCharactersRegex.IsMatch(extension); + if(isMatch) + { + return isMatch; + } + + // We want to verify here everything but the extension. + // We cannot use GetFileNameWithoutExtension because it might be that for example (..\\filename.txt) is passed in asnd that should fail, since that is not a valid filename. + fileNameToVerify = filePart.Substring(0, filePart.Length - extension.Length); + + if(String.IsNullOrEmpty(fileNameToVerify)) + { + return true; + } + } + + // We verify CLOCK$ outside the regex since for some reason the regex is not matching the clock\\$ added. + if(String.Compare(fileNameToVerify, "CLOCK$", StringComparison.OrdinalIgnoreCase) == 0) + { + return true; + } + + Regex unsafeFileNameCharactersRegex = new Regex(regexToUseForFileName, RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.CultureInvariant); + return unsafeFileNameCharactersRegex.IsMatch(fileNameToVerify); + } + + /// + /// Copy a directory recursively to the specified non-existing directory + /// + /// Directory to copy from + /// Directory to copy to + public static void RecursivelyCopyDirectory(string source, string target) + { + // Make sure it doesn't already exist + if(Directory.Exists(target)) + throw new ArgumentException(String.Format(CultureInfo.CurrentCulture, SR.GetString(SR.FileOrFolderAlreadyExists, CultureInfo.CurrentUICulture), target)); + + Directory.CreateDirectory(target); + DirectoryInfo directory = new DirectoryInfo(source); + + // Copy files + foreach(FileInfo file in directory.GetFiles()) + { + file.CopyTo(Path.Combine(target, file.Name)); + } + + // Now recurse to child directories + foreach(DirectoryInfo child in directory.GetDirectories()) + { + RecursivelyCopyDirectory(child.FullName, Path.Combine(target, child.Name)); + } + } + + /// + /// Canonicalizes a file name, including: + /// - determines the full path to the file + /// - casts to upper case + /// Canonicalizing a file name makes it possible to compare file names using simple simple string comparison. + /// + /// Note: this method does not handle shared drives and UNC drives. + /// + /// A file name, which can be relative/absolute and contain lower-case/upper-case characters. + /// Canonicalized file name. + internal static string CanonicalizeFileName(string anyFileName) + { + // Get absolute path + // Note: this will not handle UNC paths + FileInfo fileInfo = new FileInfo(anyFileName); + string fullPath = fileInfo.FullName; + + // Cast to upper-case + fullPath = fullPath.ToUpper(CultureInfo.CurrentCulture); + + return fullPath; + } + + + /// + /// Determines if a file is a template. + /// + /// The file to check whether it is a template file + /// true if the file is a template file + internal static bool IsTemplateFile(string fileName) + { + if(String.IsNullOrEmpty(fileName)) + { + return false; + } + + string extension = Path.GetExtension(fileName); + return (String.Compare(extension, ".vstemplate", StringComparison.OrdinalIgnoreCase) == 0 || string.Compare(extension, ".vsz", StringComparison.OrdinalIgnoreCase) == 0); + } + + /// + /// Retrives the configuration and the platform using the IVsSolutionBuildManager2 interface. + /// + /// A service provider. + /// The hierrachy whose configuration is requested. + /// The name of the active configuration. + /// The name of the platform. + /// true if successfull. + internal static bool TryGetActiveConfigurationAndPlatform(System.IServiceProvider serviceProvider, IVsHierarchy hierarchy, out string configuration, out string platform) + { + if(serviceProvider == null) + { + throw new ArgumentNullException("serviceProvider"); + } + + if(hierarchy == null) + { + throw new ArgumentNullException("hierarchy"); + } + + configuration = String.Empty; + platform = String.Empty; + + IVsSolutionBuildManager2 solutionBuildManager = serviceProvider.GetService(typeof(SVsSolutionBuildManager)) as IVsSolutionBuildManager2; + + if(solutionBuildManager == null) + { + return false; + } + + IVsProjectCfg[] activeConfigs = new IVsProjectCfg[1]; + ErrorHandler.ThrowOnFailure(solutionBuildManager.FindActiveProjectCfg(IntPtr.Zero, IntPtr.Zero, hierarchy, activeConfigs)); + + IVsProjectCfg activeCfg = activeConfigs[0]; + + // Can it be that the activeCfg is null? + System.Diagnostics.Debug.Assert(activeCfg != null, "Cannot find the active configuration"); + + string canonicalName; + ErrorHandler.ThrowOnFailure(activeCfg.get_CanonicalName(out canonicalName)); + + return ProjectConfig.TrySplitConfigurationCanonicalName(canonicalName, out configuration, out platform); + } + + /// + /// Determines whether the shell is in command line mode. + /// + /// A reference to a Service Provider. + /// true if the shell is in command line mode. false otherwise. + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + internal static bool IsShellInCommandLineMode(System.IServiceProvider serviceProvider) + { + if(serviceProvider == null) + { + throw new ArgumentNullException("serviceProvider"); + } + + IVsShell shell = serviceProvider.GetService(typeof(SVsShell)) as IVsShell; + if(shell == null) + { + throw new InvalidOperationException(); + } + + object isInCommandLineModeAsObject; + ErrorHandler.ThrowOnFailure(shell.GetProperty((int)__VSSPROPID.VSSPROPID_IsInCommandLineMode, out isInCommandLineModeAsObject)); + + return ((bool)isInCommandLineModeAsObject); + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/VSShellUtilities.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/VSShellUtilities.cs new file mode 100644 index 0000000000..838bcab9ef --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/VSShellUtilities.cs @@ -0,0 +1,71 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using Microsoft.VisualStudio; +using Microsoft.VisualStudio.Shell.Interop; + +namespace Microsoft.VisualStudio.Project +{ + /// + ///This class provides some useful static shell based methods. + /// + [CLSCompliant(false)] + public static class UIHierarchyUtilities + { + /// + /// Get reference to IVsUIHierarchyWindow interface from guid persistence slot. + /// + /// The service provider. + /// Unique identifier for a tool window created using IVsUIShell::CreateToolWindow. + /// The caller of this method can use predefined identifiers that map to tool windows if those tool windows + /// are known to the caller. + /// A reference to an IVsUIHierarchyWindow interface. + public static IVsUIHierarchyWindow GetUIHierarchyWindow(IServiceProvider serviceProvider, Guid persistenceSlot) + { + if(serviceProvider == null) + { + throw new ArgumentNullException("serviceProvider"); + } + + IVsUIShell shell = serviceProvider.GetService(typeof(SVsUIShell)) as IVsUIShell; + + Debug.Assert(shell != null, "Could not get the ui shell from the project"); + if(shell == null) + { + throw new InvalidOperationException(); + } + + object pvar = null; + IVsWindowFrame frame = null; + IVsUIHierarchyWindow uiHierarchyWindow = null; + + try + { + ErrorHandler.ThrowOnFailure(shell.FindToolWindow(0, ref persistenceSlot, out frame)); + ErrorHandler.ThrowOnFailure(frame.GetProperty((int)__VSFPROPID.VSFPROPID_DocView, out pvar)); + } + finally + { + if(pvar != null) + { + uiHierarchyWindow = (IVsUIHierarchyWindow)pvar; + } + } + + return uiHierarchyWindow; + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/VisualStudio.Project.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/VisualStudio.Project.cs new file mode 100644 index 0000000000..eb6c44fd7e --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/VisualStudio.Project.cs @@ -0,0 +1,276 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Reflection; +using System.Globalization; +using System.Resources; +using System.Text; +using System.Threading; +using System.ComponentModel; +using System.Security.Permissions; + +namespace Microsoft.VisualStudio.Project +{ + [AttributeUsage(AttributeTargets.All)] + internal sealed class SRDescriptionAttribute : DescriptionAttribute + { + private bool replaced; + + public SRDescriptionAttribute(string description) + : base(description) + { + } + + public override string Description + { + get + { + if(!replaced) + { + replaced = true; + DescriptionValue = SR.GetString(base.Description, CultureInfo.CurrentUICulture); + } + return base.Description; + } + } + } + + [AttributeUsage(AttributeTargets.All)] + internal sealed class SRCategoryAttribute : CategoryAttribute + { + + public SRCategoryAttribute(string category) + : base(category) + { + } + + protected override string GetLocalizedString(string value) + { + return SR.GetString(value, CultureInfo.CurrentUICulture); + } + } + internal sealed class SR + { + internal const string AddReferenceDialogTitle = "AddReferenceDialogTitle"; + internal const string AddToNullProjectError = "AddToNullProjectError"; + internal const string Advanced = "Advanced"; + internal const string AssemblyReferenceAlreadyExists = "AssemblyReferenceAlreadyExists"; + internal const string AttributeLoad = "AttributeLoad"; + internal const string BuildAction = "BuildAction"; + internal const string BuildActionDescription = "BuildActionDescription"; + internal const string BuildCaption = "BuildCaption"; + internal const string BuildVerbosity = "BuildVerbosity"; + internal const string BuildVerbosityDescription = "BuildVerbosityDescription"; + internal const string BuildEventError = "BuildEventError"; + internal const string CancelQueryEdit = "CancelQueryEdit"; + internal const string CannotAddFileThatIsOpenInEditor = "CannotAddFileThatIsOpenInEditor"; + internal const string CanNotSaveFileNotOpeneInEditor = "CanNotSaveFileNotOpeneInEditor"; + internal const string cli1 = "cli1"; + internal const string Compile = "Compile"; + internal const string ConfirmExtensionChange = "ConfirmExtensionChange"; + internal const string Content = "Content"; + internal const string CopyToLocal = "CopyToLocal"; + internal const string CopyToLocalDescription = "CopyToLocalDescription"; + internal const string CustomTool = "CustomTool"; + internal const string CustomToolDescription = "CustomToolDescription"; + internal const string CustomToolNamespace = "CustomToolNamespace"; + internal const string CustomToolNamespaceDescription = "CustomToolNamespaceDescription"; + internal const string DetailsImport = "DetailsImport"; + internal const string DetailsUserImport = "DetailsUserImport"; + internal const string DetailsItem = "DetailsItem"; + internal const string DetailsItemLocation = "DetailsItemLocation"; + internal const string DetailsProperty = "DetailsProperty"; + internal const string DetailsTarget = "DetailsTarget"; + internal const string DetailsUsingTask = "DetailsUsingTask"; + internal const string Detailed = "Detailed"; + internal const string Diagnostic = "Diagnostic"; + internal const string DirectoryExistError = "DirectoryExistError"; + internal const string EditorViewError = "EditorViewError"; + internal const string EmbeddedResource = "EmbeddedResource"; + internal const string Error = "Error"; + internal const string ErrorInvalidFileName = "ErrorInvalidFileName"; + internal const string ErrorInvalidProjectName = "ErrorInvalidProjectName"; + internal const string ErrorReferenceCouldNotBeAdded = "ErrorReferenceCouldNotBeAdded"; + internal const string ErrorMsBuildRegistration = "ErrorMsBuildRegistration"; + internal const string ErrorSaving = "ErrorSaving"; + internal const string Exe = "Exe"; + internal const string ExpectedObjectOfType = "ExpectedObjectOfType"; + internal const string FailedToGetService = "FailedToGetService"; + internal const string FailedToRetrieveProperties = "FailedToRetrieveProperties"; + internal const string FileNameCannotContainALeadingPeriod = "FileNameCannotContainALeadingPeriod"; + internal const string FileCannotBeRenamedToAnExistingFile = "FileCannotBeRenamedToAnExistingFile"; + internal const string FileAlreadyExistsAndCannotBeRenamed = "FileAlreadyExistsAndCannotBeRenamed"; + internal const string FileAlreadyExists = "FileAlreadyExists"; + internal const string FileAlreadyExistsCaption = "FileAlreadyExistsCaption"; + internal const string FileAlreadyInProject = "FileAlreadyInProject"; + internal const string FileAlreadyInProjectCaption = "FileAlreadyInProjectCaption"; + internal const string FileCopyError = "FileCopyError"; + internal const string FileName = "FileName"; + internal const string FileNameDescription = "FileNameDescription"; + internal const string FileOrFolderAlreadyExists = "FileOrFolderAlreadyExists"; + internal const string FileOrFolderCannotBeFound = "FileOrFolderCannotBeFound"; + internal const string FileProperties = "FileProperties"; + internal const string FolderName = "FolderName"; + internal const string FolderNameDescription = "FolderNameDescription"; + internal const string FolderProperties = "FolderProperties"; + internal const string FullPath = "FullPath"; + internal const string FullPathDescription = "FullPathDescription"; + internal const string ItemDoesNotExistInProjectDirectory = "ItemDoesNotExistInProjectDirectory"; + internal const string InvalidAutomationObject = "InvalidAutomationObject"; + internal const string InvalidLoggerType = "InvalidLoggerType"; + internal const string InvalidParameter = "InvalidParameter"; + internal const string Library = "Library"; + internal const string LinkedItemsAreNotSupported = "LinkedItemsAreNotSupported"; + internal const string Minimal = "Minimal"; + internal const string Misc = "Misc"; + internal const string None = "None"; + internal const string Normal = "Normal"; + internal const string NestedProjectFailedToReload = "NestedProjectFailedToReload"; + internal const string OutputPath = "OutputPath"; + internal const string OutputPathDescription = "OutputPathDescription"; + internal const string PasteFailed = "PasteFailed"; + internal const string ParameterMustBeAValidGuid = "ParameterMustBeAValidGuid"; + internal const string ParameterMustBeAValidItemId = "ParameterMustBeAValidItemId"; + internal const string ParameterCannotBeNullOrEmpty = "ParameterCannotBeNullOrEmpty"; + internal const string PathTooLong = "PathTooLong"; + internal const string ProjectContainsCircularReferences = "ProjectContainsCircularReferences"; + internal const string Program = "Program"; + internal const string Project = "Project"; + internal const string ProjectFile = "ProjectFile"; + internal const string ProjectFileDescription = "ProjectFileDescription"; + internal const string ProjectFolder = "ProjectFolder"; + internal const string ProjectFolderDescription = "ProjectFolderDescription"; + internal const string ProjectProperties = "ProjectProperties"; + internal const string Quiet = "Quiet"; + internal const string QueryReloadNestedProject = "QueryReloadNestedProject"; + internal const string ReferenceAlreadyExists = "ReferenceAlreadyExists"; + internal const string ReferencesNodeName = "ReferencesNodeName"; + internal const string ReferenceProperties = "ReferenceProperties"; + internal const string RefName = "RefName"; + internal const string RefNameDescription = "RefNameDescription"; + internal const string RenameFolder = "RenameFolder"; + internal const string RTL = "RTL"; + internal const string SaveCaption = "SaveCaption"; + internal const string SaveModifiedDocuments = "SaveModifiedDocuments"; + internal const string SaveOfProjectFileOutsideCurrentDirectory = "SaveOfProjectFileOutsideCurrentDirectory"; + internal const string StandardEditorViewError = "StandardEditorViewError"; + internal const string Settings = "Settings"; + internal const string URL = "URL"; + internal const string UseOfDeletedItemError = "UseOfDeletedItemError"; + internal const string v1 = "v1"; + internal const string v11 = "v11"; + internal const string v2 = "v2"; + internal const string v3 = "v3"; + internal const string v35 = "v35"; + internal const string v4 = "v4"; + internal const string Warning = "Warning"; + internal const string WinExe = "WinExe"; + + static SR loader; + ResourceManager resources; + + private static Object s_InternalSyncObject; + private static Object InternalSyncObject + { + get + { + if(s_InternalSyncObject == null) + { + Object o = new Object(); + Interlocked.CompareExchange(ref s_InternalSyncObject, o, null); + } + return s_InternalSyncObject; + } + } + + internal SR() + { + resources = new System.Resources.ResourceManager("Microsoft.VisualStudio.Project", this.GetType().Assembly); + } + + private static SR GetLoader() + { + if(loader == null) + { + lock(InternalSyncObject) + { + if(loader == null) + { + loader = new SR(); + } + } + } + + return loader; + } + + private static CultureInfo Culture + { + get { return null/*use ResourceManager default, CultureInfo.CurrentUICulture*/; } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static ResourceManager Resources + { + get + { + return GetLoader().resources; + } + } + + public static string GetString(string name, params object[] args) + { + SR sys = GetLoader(); + if(sys == null) + return null; + string res = sys.resources.GetString(name, SR.Culture); + + if(args != null && args.Length > 0) + { + return String.Format(CultureInfo.CurrentCulture, res, args); + } + else + { + return res; + } + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static string GetString(string name) + { + SR sys = GetLoader(); + if(sys == null) + return null; + return sys.resources.GetString(name, SR.Culture); + } + + public static string GetString(string name, CultureInfo culture) + { + SR sys = GetLoader(); + if(sys == null) + return null; + return sys.resources.GetString(name, culture); + } + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")] + public static object GetObject(string name) + { + SR sys = GetLoader(); + if(sys == null) + return null; + return sys.resources.GetObject(name, SR.Culture); + } + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/VisualStudio.Project.resx b/Tools/IronStudio/IronStudio/VisualStudio/Project/VisualStudio.Project.resx new file mode 100644 index 0000000000..e9f002b33b --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/VisualStudio.Project.resx @@ -0,0 +1,521 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + text/microsoft-resx + + + 2.0 + + + System.Resources.ResXResourceReader, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + System.Resources.ResXResourceWriter, System.Windows.Forms, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089 + + + Failed to add file '{0}' to project as project is null. + + + Advanced + Project Property Page Caption + + + A reference to component '{0}' cannot be added. A reference to the component already exists in the project. + ReferenceAlreadyExists error message + + + Could not load attribute '{0}' from project file '{1}'. + + + Build Action + Project Build Property Name + + + How the file relates to the build and deployment process + Project Build Property Description + + + Verbosity + + + Specify how much information is included in the build output + + + ECMA-335 CLI compatible framework (location must be provided) + Target platform drop down option + + + Compile + Build Action - drop down option + + + If you change a file name extension, the file may become unusable. Are you sure you want to change it? + + + A file with name '{0}' already exists and is open in an editor. Please give a unique name to the item you are adding, or delete the existing item first. + + + Cannot save '{0}' as it is not open in the editor. + + + Content + Build Action - drop down option + + + Copy Local + File property + + + Indicates whether the reference will be copied to the output directory. + + + Detailed + + + Diagnostic + + + Directory already exists + + + Error opening specified view '{0}' using editor '{1}' + + + Embedded Resource + Build Action - drop down option + + + error + + + Files and folders cannot be: +- Empty strings +- System reserved names, including 'CON', 'AUX', PRN', 'COM1' or 'LPT2' +- contain only '.' +- have any of the following characters: / ? : & \ * " < > | # % + + + The name you provided is not a valid project name. + + + MSBuild path not found in registry. Please reinstall to fix the problem. + + + Error Saving File + + + Console Application + + + Expecting object of type {0}. + + + Failed to retrieve MSBuild property {0} from the project file. + The exception message thrown when getting a property from msbuild fails. + + + A file with the same name '{0}' already exists. Do you want to overwrite it? + + + A file with the same name '{0}' already exists. + + + Destination File Exists + + + A file of this name is already part of the project. Do you want to overwrite it? + + + Destination File Exists + + + A file or folder with the name '{0}' already exists on disk at this location. Please choose another name. + + + Error Copying File + + + File Name + + + File and folder names cannot contain a leading period. + + + The name of the file or folder + + + Folder Name + + + Name of this folder + + + Full Path + + + Location of the file + + + The item '{0}' does not exist in the project directory. It may have been moved, renamed or deleted. + + + Cannot save '{0}' outside the project directory. Linked items are not supported. + + + Class Library + + + Minimal + + + Misc + + + None + + + Normal + + + Item is not available or corrupted and thus cannot be pasted. + + + Program + + + Project + + + A reference to library '{0}' cannot be added. Adding this project as a reference would cause a circular dependency. + + + Project File + + + The name of the file containing build, configuration, and other information about the project. + + + Project Folder + + + The absolute location of the project. + + + Quiet + + + (Name) + + + References + + + Display name of the reference + + + Rename directory failed. {0} + + + RTL_False + + + Save? + + + Do you want to save modified documents? + + + The project file can only be saved into the project location '{0}'. + + + Error opening specified view '{0}' using standard editor. + + + URL + + + You are trying to use an item that has already been deleted or that does not exist. + + + Microsoft .NET Framework v1.0 + + + Microsoft .NET Framework v1.1 + + + Microsoft .NET Framework v2.0 + + + warning + + + Windows Application + + + Add Reference + + + Automation object invalid. + + + The command you are attempting cannot be completed because the file '{0}' that must be modified cannot be changed. If the file is under source control, you may want to check it out; if the file is read-only on disk, you may want to change its attributes. + MsgBox + + + Parameter must be a valid guid. + Error message in the ArgumentException for a parameter that is not a valid guid. + + + Invalid logger type\nExpected: {0}\nReceived: {1} + Error message thrown for when an invalid logger type is set. + + + InvalidParameter + Generic error message for invalid parameters. + + + ParameterCannotBeNullOEmpty + Error message for string parameters that cannot be null or empty. + + + Parameter must be a valid item identifier. + Error message thrown when an invalid item id is retrieved. + + + File Properties + + + Folder Properties + + + Project Properties + + + Reference Properties + + + A file or folder with the name '{0}' already exists on disk at this location. Please choose another name. + + + Build + + + Output Path + + + The path to the primary output + + + The complete path to '{0}' exceeds the maximum number of characters permitted by the file system. + + + Custom Tool + + + Specifies the tool that transforms a file at design time and places the output of that transformation into another file. For example, a dataset (.xsd) file comes with a default custom tool. + + + Custom Tool Namespace + + + The namespace into which the output of the custom tool is placed. + + + Primary Output + + + Content Files + + + Documentation Files + + + Localized Resources + + + Source Files + + + Debug Symbols + + + XML Serialization Assemblies + + + Contains the DLL or EXE built by the project. + + + Contains all content files in the project. + + + Contains the XML Documentation files for the project. + + + Contains the satellite assemblies for each culture's resources. + + + Contains all source files in the project. + + + Contains the debugging files for the project. + + + Contains the XML serialization assemblies for the project. + + + The project file '{0}' has been modified outside of Visual Studio. Do you want to reload the project? + + + The nested project has failed to reload. + + + The item '{0}' cannot be found on disk, either because it has been renamed, deleted, or moved to a new location. + + + A reference to {0} could not be added. Please make sure that the file is accessible, and that it is a valid assembly or COM component. + + + An 'Import' of the file '{1}' was found in the project file '{0}'. This file is not registered as a safe file to import, and could contain targets and tasks that are harmful. If this imported file is indeed considered safe, then it can be registered by writing to the registry key {2}. + + + An 'Import' of the file '{1}' was found in the user project file '{0}'. All imports in user project files are considered unsafe. + + + One or more items named '{1}' were found in the project file '{0}'. These items normally should not be specified in the project file. They can change the way targets and tasks are executed during project load, and this could have harmful effects. + + + A property named '{1}' was found in the project file '{0}'. This property normally should not be specified in the project file. Its value can change the way targets and tasks are executed during project load, and this could have harmful effects. + + + A 'UsingTask' tag which registers the '{1}' task was found in the project file '{0}'. 'UsingTask' tags in the project file take precedence over those in the imported .TARGETS files, and therefore could be used to execute arbitrary code during an otherwise unmodified build process. + + + A 'Target' named '{1}' was found in the project file '{0}'. The tasks within this target could contain arbitrary code and may get executed as soon as the project is loaded in the IDE. + + + An item referring to the file '{1}' was found in the project file '{0}'. Since this file is located within a system directory, root directory, or network share, it could be harmful to write to this file. + + + The project location is not trusted:{0}{0}{1}{0}{0}Running the application may result in security exceptions when it {0}attempts to perform actions which require full trust.{0}{0}Click OK to ignore and continue. + + + Exception was thrown during BuildBegin event\n{0} + + + Settings + + + Microsoft .NET Framework v3.0 + + + Microsoft .NET Framework v3.5 + + + Microsoft .NET Framework v4.0 + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/VsCommands.cs b/Tools/IronStudio/IronStudio/VisualStudio/Project/VsCommands.cs new file mode 100644 index 0000000000..686be5f1bf --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/VsCommands.cs @@ -0,0 +1,106 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics.CodeAnalysis; + +namespace Microsoft.VisualStudio.Project +{ + /// + /// Defines menu commands guids and menu command id's + /// + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Design", "CA1053:StaticHolderTypesShouldNotHaveConstructors"), SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "Vs")] + public class VsMenus + { + // menu command guids. + [SuppressMessage("Microsoft.Usage", "CA2211:NonConstantFieldsShouldNotBeVisible")] + public static Guid guidStandardCommandSet97 = new Guid("5efc7975-14bc-11cf-9b2b-00aa00573819"); + + [SuppressMessage("Microsoft.Usage", "CA2211:NonConstantFieldsShouldNotBeVisible")] + public static Guid guidStandardCommandSet2K = new Guid("1496A755-94DE-11D0-8C3F-00C04FC2AAE2"); + + [SuppressMessage("Microsoft.Usage", "CA2211:NonConstantFieldsShouldNotBeVisible")] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Pkg")] + public static Guid guidVsVbaPkg = new Guid(0xa659f1b3, 0xad34, 0x11d1, 0xab, 0xad, 0x0, 0x80, 0xc7, 0xb8, 0x9c, 0x95); + + [SuppressMessage("Microsoft.Usage", "CA2211:NonConstantFieldsShouldNotBeVisible")] + public static Guid guidSHLMainMenu = new Guid(0xd309f791, 0x903f, 0x11d0, 0x9e, 0xfc, 0x00, 0xa0, 0xc9, 0x11, 0x00, 0x4f); + + [SuppressMessage("Microsoft.Usage", "CA2211:NonConstantFieldsShouldNotBeVisible")] + public static Guid guidVSUISet = new Guid("60481700-078b-11d1-aaf8-00a0c9055a90"); + + [SuppressMessage("Microsoft.Usage", "CA2211:NonConstantFieldsShouldNotBeVisible")] + [SuppressMessage("Microsoft.Naming", "CA1704:IdentifiersShouldBeSpelledCorrectly", MessageId = "Cmds")] + public static Guid guidVsUIHierarchyWindowCmds = new Guid("60481700-078B-11D1-AAF8-00A0C9055A90"); + + // Special Menus. + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CODEWIN")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "IDM")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CTXT")] + [SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores")] + public const int IDM_VS_CTXT_CODEWIN = 0x040D; + + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "ITEMNODE")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "IDM")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CTXT")] + [SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores")] + public const int IDM_VS_CTXT_ITEMNODE = 0x0430; + + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "PROJNODE")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "IDM")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CTXT")] + [SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores")] + public const int IDM_VS_CTXT_PROJNODE = 0x0402; + + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "REFERENCEROOT")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "IDM")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CTXT")] + [SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores")] + public const int IDM_VS_CTXT_REFERENCEROOT = 0x0450; + + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "REFERENCE")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "IDM")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CTXT")] + [SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores")] + public const int IDM_VS_CTXT_REFERENCE = 0x0451; + + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "FOLDERNODE")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "IDM")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CTXT")] + [SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores")] + public const int IDM_VS_CTXT_FOLDERNODE = 0x0431; + + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "NOCOMMANDS")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "IDM")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CTXT")] + [SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores")] + public const int IDM_VS_CTXT_NOCOMMANDS = 0x041A; + + public const int VSCmdOptQueryParameterList = 1; + + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "MULTIITEM")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "IDM")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CTXT")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "XPROJ")] + [SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores")] + public const int IDM_VS_CTXT_XPROJ_MULTIITEM = 0x0419; + + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "PROJITEM")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "IDM")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "CTXT")] + [SuppressMessage("Microsoft.Naming", "CA1709:IdentifiersShouldBeCasedCorrectly", MessageId = "XPROJ")] + [SuppressMessage("Microsoft.Naming", "CA1707:IdentifiersShouldNotContainUnderscores")] + public const int IDM_VS_CTXT_XPROJ_PROJITEM = 0x0417; + } +} diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/WebProjectBase.Files b/Tools/IronStudio/IronStudio/VisualStudio/Project/WebProjectBase.Files new file mode 100644 index 0000000000..5feb00be0b --- /dev/null +++ b/Tools/IronStudio/IronStudio/VisualStudio/Project/WebProjectBase.Files @@ -0,0 +1,32 @@ + + + + + + + + + + + ProjectBase\Web\CodeBehindCodeGenerator.cs + true + + + ProjectBase\Web\FieldData.cs + true + + + ProjectBase\Web\LockedDocData.cs + true + + + ProjectBase\Web\VsHierarchyItem.cs + true + + + ProjectBase\Web\WAUtilities.cs + true + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudio/VisualStudio/Project/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache b/Tools/IronStudio/IronStudio/VisualStudio/Project/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache new file mode 100644 index 0000000000..77bc0abcb8 Binary files /dev/null and b/Tools/IronStudio/IronStudio/VisualStudio/Project/obj/Debug/DesignTimeResolveAssemblyReferencesInput.cache differ diff --git a/Tools/IronStudio/IronStudio/source.extension.vsixmanifest b/Tools/IronStudio/IronStudio/source.extension.vsixmanifest new file mode 100644 index 0000000000..e77e37b526 --- /dev/null +++ b/Tools/IronStudio/IronStudio/source.extension.vsixmanifest @@ -0,0 +1,31 @@ + + + + IronStudio + Microsoft + 1.0 + + 1033 + http://ironpython.codeplex.com + http://ironpython.codeplex.com + false + + + Ultimate + Premium + Pro + IntegratedShell + VSTS + VSTD + + + + + + + |%CurrentProject%| + IronStudio + |%CurrentProject%| + |IronStudioCore| + + diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore.csproj b/Tools/IronStudio/IronStudioCore/IronStudioCore.csproj new file mode 100644 index 0000000000..bf88433997 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore.csproj @@ -0,0 +1,167 @@ + + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {843716AE-38B3-4723-963C-950DD06BC4B8} + Library + Properties + Microsoft.IronStudio.Core + IronStudio.Core + v4.0 + 512 + SAK + SAK + SAK + SAK + Client + + + true + full + false + $(SolutionDir)..\bin\Debug + DEBUG;TRACE;$(SignedSym) + prompt + 4 + + + pdbonly + true + $(SolutionDir)..\bin\Release + TRACE;$(SignedSym) + prompt + 4 + + + + + False + $(DevEnvDir)\PublicAssemblies\Microsoft.VisualStudio.ComponentModelHost.dll + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.CoreUtility.dll + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Language.Intellisense.dll + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Language.StandardClassification.dll + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Text.Data.dll + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Text.Logic.dll + + + False + $(VSSDK100Install)\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Text.UI.dll + + + False + $(VSSDK100Install)\Microsoft Visual Studio 2010 SDK\VisualStudioIntegration\Common\Assemblies\v4.0\Microsoft.VisualStudio.Text.UI.Wpf.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Dynamic + + + {02FF0909-F5AD-48CF-A86A-345E721B7E40} + Microsoft.Scripting + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore.csproj.vspscc b/Tools/IronStudio/IronStudioCore/IronStudioCore.csproj.vspscc new file mode 100644 index 0000000000..feffdecaa4 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/CoreConstants.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/CoreConstants.cs new file mode 100644 index 0000000000..9f2b711d5b --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/CoreConstants.cs @@ -0,0 +1,29 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.IronStudio { + public static class CoreConstants { + public const string DlrContentTypeName = "DlrCode"; + + /// + /// The additional role found in any REPL editor window. + /// + public const string ReplTextViewRole = "REPL"; + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/CoreUtils.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/CoreUtils.cs new file mode 100644 index 0000000000..ef5b7ee5fd --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/CoreUtils.cs @@ -0,0 +1,36 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronStudio.Core { + public static class CoreUtils { + public static void RegisterExtensions( + IContentTypeRegistryService/*!*/ contentTypeRegistryService, + IFileExtensionRegistryService/*!*/ fileExtensionRegistryService, + IContentType/*!*/ contentType, + IEnumerable/*!*/ fileExtensions + ) { + foreach (var extension in fileExtensions) { + if (fileExtensionRegistryService.GetContentTypeForExtension(extension) == contentTypeRegistryService.UnknownContentType) { + fileExtensionRegistryService.AddFileExtension(extension, contentType); + } + } + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Dlr.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Dlr.cs new file mode 100644 index 0000000000..2f3bdea21a --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Dlr.cs @@ -0,0 +1,27 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel.Composition; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronStudio.Core { + /// + /// Registers the DLR content type so which is the base type for all DLR hosting API based languages. + /// + internal static class Dlr { + + [Export, Name(CoreConstants.DlrContentTypeName), BaseDefinition("code")] + internal static ContentTypeDefinition DlrCodeContentType = null; + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/DlrClassifier.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/DlrClassifier.cs new file mode 100644 index 0000000000..82c007424a --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/DlrClassifier.cs @@ -0,0 +1,274 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Linq; +using System.Collections.Generic; +using Microsoft.IronStudio.Library; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Tagging; +using System.ComponentModel.Composition; +using System.Diagnostics.Contracts; +using System.Diagnostics; + +namespace Microsoft.IronStudio.Core { + /// + /// Provides classification based upon the DLR TokenCategory enum. + /// + internal class DlrClassifier : IDlrClassifier { + private readonly TokenCache _tokenCache; + private readonly DlrClassifierProvider _provider; + private readonly TokenCategorizer _categorizer; + private readonly ScriptEngine _engine; + private readonly ITextBuffer _buffer; + + internal DlrClassifier(DlrClassifierProvider provider, ScriptEngine engine, ITextBuffer buffer) { + buffer.Changed += BufferChanged; + buffer.ContentTypeChanged += BufferContentTypeChanged; + + _tokenCache = new TokenCache(); + _categorizer = engine.GetService(); + _engine = engine; + _provider = provider; + _buffer = buffer; + } + + #region IDlrClassifier + + // This event gets raised if the classification of existing test changes. + public event EventHandler ClassificationChanged; + + /// + /// This method classifies the given snapshot span. + /// + public IList GetClassificationSpans(SnapshotSpan span) { + var classifications = new List(); + var snapshot = span.Snapshot; + + IMixedBuffer mixedBuffer; + if (snapshot.TextBuffer.Properties.TryGetProperty(typeof(IMixedBuffer), out mixedBuffer)) { + foreach (SnapshotSpan codeSpan in mixedBuffer.GetLanguageSpans(snapshot)) { + SnapshotSpan? intersection = codeSpan.Overlap(span); + if (intersection != null) { + var firstCodeLine = codeSpan.Start.GetContainingLine(); + AddClassifications(classifications, intersection.Value, firstCodeLine.LineNumber, codeSpan.Start - firstCodeLine.Start); + } + } + } else if(span.Length > 0) { + AddClassifications(classifications, span, 0, 0); + } + + return classifications; + } + + public IDlrClassifierProvider Provider { + get { + return _provider; + } + } + + #endregion + + #region Private Members + + private Dictionary CategoryMap { + get { + return _provider.CategoryMap; + } + } + + private void BufferContentTypeChanged(object sender, ContentTypeChangedEventArgs e) { + _tokenCache.Clear(); + _buffer.Changed -= BufferChanged; + _buffer.ContentTypeChanged -= BufferContentTypeChanged; + _buffer.Properties.RemoveProperty(typeof(IDlrClassifier)); + } + + private void BufferChanged(object sender, TextContentChangedEventArgs e) { + var snapshot = e.After; + + IMixedBuffer mixedBuffer; + snapshot.TextBuffer.Properties.TryGetProperty(typeof(IMixedBuffer), out mixedBuffer); + + _tokenCache.EnsureCapacity(snapshot.LineCount); + + foreach (var change in e.Changes) { + if (change.LineCountDelta > 0) { + _tokenCache.InsertLines(snapshot.GetLineNumberFromPosition(change.NewEnd) + 1 - change.LineCountDelta, change.LineCountDelta); + } else if (change.LineCountDelta < 0) { + _tokenCache.DeleteLines(snapshot.GetLineNumberFromPosition(change.NewEnd) + 1, -change.LineCountDelta); + } + + if (mixedBuffer != null) { + foreach (SnapshotSpan codeSpan in mixedBuffer.GetLanguageSpans(snapshot)) { + // we want the intersection here because we care about empty spans for deletes. + SnapshotSpan? intersection = codeSpan.Intersection(change.NewSpan); + if (intersection != null) { + var firstCodeLine = codeSpan.Start.GetContainingLine(); + ApplyChange(snapshot, intersection.Value.Span, firstCodeLine.LineNumber, codeSpan.Start - firstCodeLine.Start); + } + } + } else { + ApplyChange(snapshot, change.NewSpan, 0, 0); + } + } + } + + /// + /// Adds classification spans to the given collection. + /// Scans a contiguous sub- of a larger code span which starts at . + /// + private void AddClassifications(List classifications, SnapshotSpan span, int codeStartLine, int codeStartLineOffset) { + Debug.Assert(span.Length > 0); + + var snapshot = span.Snapshot; + int firstLine = snapshot.GetLineNumberFromPosition(span.Start); + int lastLine = snapshot.GetLineNumberFromPosition(span.End - 1); + + Contract.Assert(codeStartLineOffset >= 0); + Contract.Assert(firstLine >= codeStartLine); + + _tokenCache.EnsureCapacity(snapshot.LineCount); + + // find the closest line preceding firstLine for which we know categorizer state, stop at the codeStartLine: + LineTokenization lineTokenization; + int currentLine = _tokenCache.IndexOfPreviousTokenization(firstLine, codeStartLine, out lineTokenization) + 1; + object state = lineTokenization.State; + + while (currentLine <= lastLine) { + if (!_tokenCache.TryGetTokenization(currentLine, out lineTokenization)) { + lineTokenization = TokenizeLine(snapshot, state, currentLine, (currentLine == codeStartLine) ? codeStartLineOffset : 0); + _tokenCache[currentLine] = lineTokenization; + } + + state = lineTokenization.State; + + classifications.AddRange( + from token in lineTokenization.Tokens + let classification = ClassifyToken(span, token, currentLine) + where classification != null + select classification + ); + + currentLine++; + } + } + + /// + /// Rescans the part of the buffer affected by a change. + /// Scans a contiguous sub- of a larger code span which starts at . + /// + private void ApplyChange(ITextSnapshot snapshot, Span span, int codeStartLine, int codeStartLineOffset) { + int firstLine = snapshot.GetLineNumberFromPosition(span.Start); + int lastLine = snapshot.GetLineNumberFromPosition(span.Length > 0 ? span.End - 1 : span.End); + + Contract.Assert(codeStartLineOffset >= 0); + Contract.Assert(firstLine >= codeStartLine); + + // find the closest line preceding firstLine for which we know categorizer state, stop at the codeStartLine: + LineTokenization lineTokenization; + firstLine = _tokenCache.IndexOfPreviousTokenization(firstLine, codeStartLine, out lineTokenization) + 1; + object state = lineTokenization.State; + + int currentLine = firstLine; + object previousState; + while (currentLine < snapshot.LineCount) { + previousState = _tokenCache.TryGetTokenization(currentLine, out lineTokenization) ? lineTokenization.State : null; + _tokenCache[currentLine] = lineTokenization = TokenizeLine(snapshot, state, currentLine, (currentLine == codeStartLine) ? codeStartLineOffset : 0); + state = lineTokenization.State; + + // stop if we visted all affected lines and the current line has no tokenization state or its previous state is the same as the new state: + if (currentLine > lastLine && (previousState == null || previousState.Equals(state))) { + break; + } + + currentLine++; + } + + // classification spans might have changed between the start of the first and end of the last visited line: + int changeStart = snapshot.GetLineFromLineNumber(firstLine).Start; + int changeEnd = (currentLine < snapshot.LineCount) ? snapshot.GetLineFromLineNumber(currentLine).End : snapshot.Length; + if (changeStart < changeEnd) { + var classificationChanged = ClassificationChanged; + if (classificationChanged != null) { + var args = new ClassificationChangedEventArgs(new SnapshotSpan(snapshot, new Span(changeStart, changeEnd - changeStart))); + classificationChanged(this, args); + } + } + } + + internal LineTokenization TokenizeLine(ITextSnapshot snapshot, object previousLineState, int lineNo, int lineOffset) { + ITextSnapshotLine line = snapshot.GetLineFromLineNumber(lineNo); + SnapshotSpan lineSpan = new SnapshotSpan(snapshot, line.Start + lineOffset, line.LengthIncludingLineBreak - lineOffset); + + var tcp = new SnapshotSpanTextContentProvider(lineSpan); + var scriptSource = _engine.CreateScriptSource(tcp, null, SourceCodeKind.File); + + _categorizer.Initialize(previousLineState, scriptSource, new SourceLocation(lineOffset, lineNo + 1, lineOffset + 1)); + var tokens = new List(_categorizer.ReadTokens(lineSpan.Length)).ToArray(); + return new LineTokenization(tokens, _categorizer.CurrentState); + } + + private ClassificationSpan ClassifyToken(SnapshotSpan span, TokenInfo token, int lineNumber) { + IClassificationType classification = null; + + if (token.Category == TokenCategory.Operator) { + if (token.Trigger == TokenTriggers.MemberSelect) { + classification = _provider.DotClassification; + } + } else if (token.Category == TokenCategory.Grouping) { + if (token.Trigger == (TokenTriggers.MatchBraces | TokenTriggers.ParameterStart)) { + classification = _provider.OpenGroupingClassification; + } else if (token.Trigger == (TokenTriggers.MatchBraces | TokenTriggers.ParameterEnd)) { + classification = _provider.CloseGroupingClassification; + } + } else if (token.Category == TokenCategory.Delimiter) { + if (token.Trigger == TokenTriggers.ParameterNext) { + classification = _provider.CommaClassification; + } + } + + if (classification == null) { + CategoryMap.TryGetValue(token.Category, out classification); + } + + if (classification != null) { + var line = span.Snapshot.GetLineFromLineNumber(lineNumber); + var index = line.Start.Position + token.SourceSpan.Start.Column - 1; + var tokenSpan = new Span(index, token.SourceSpan.Length); + var intersection = span.Intersection(tokenSpan); + if (intersection != null && intersection.Value.Length > 0) { + return new ClassificationSpan(new SnapshotSpan(span.Snapshot, tokenSpan), classification); + } + } + + return null; + } + + #endregion + } + + public static class DlrClassifierExtensions { + public static IDlrClassifier GetDlrClassifier(this ITextBuffer buffer) { + IDlrClassifier res; + if (buffer.Properties.TryGetProperty(typeof(IDlrClassifier), out res)) { + return res; + } + return null; + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/DlrClassifierProvider.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/DlrClassifierProvider.cs new file mode 100644 index 0000000000..52c739d69b --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/DlrClassifierProvider.cs @@ -0,0 +1,222 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.ComponentModel.Composition; +using Microsoft.IronStudio.Core; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.VisualStudio.Language.StandardClassification; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronStudio.Core { + /// + /// Implements classification of text by using a ScriptEngine which supports the + /// TokenCategorizer service. + /// + /// Languages should subclass this type and override the Engine property. They + /// should then export the provider using MEF indicating the content type + /// which it is applicable to. + /// + /// Languages can also export this under the the content type "DlrCode" and can then + /// override the ContentType property to indicate a specific content + /// type which they provide tokenization for. This specific content type should be + /// a sub-type of DlrCode. This enables the languages to dynamicly register their + /// own content type but statically export the provider. + /// + public abstract class DlrClassifierProvider : IDlrClassifierProvider { + private Dictionary _categoryMap; + private IClassificationType _comment; + private IClassificationType _stringLiteral; + private IClassificationType _keyword; + private IClassificationType _operator; + private IClassificationType _openGroupingClassification; + private IClassificationType _loseGroupingClassification; + private IClassificationType _dotClassification; + private IClassificationType _commaClassification; + + /// + /// Import the classification registry to be used for getting a reference + /// to the custom classification type later. + /// + [Import] + public IClassificationTypeRegistryService _classificationRegistry = null; // Set via MEF + + #region DLR Classification Type Definitions + + [Export] + [Name(DlrPredefinedClassificationTypeNames.OpenGrouping)] + [BaseDefinition(PredefinedClassificationTypeNames.Operator)] + internal static ClassificationTypeDefinition OpenGroupingClassificationDefinition = null; // Set via MEF + + [Export] + [Name(DlrPredefinedClassificationTypeNames.CloseGrouping)] + [BaseDefinition(PredefinedClassificationTypeNames.Operator)] + internal static ClassificationTypeDefinition CloseGroupingClassificationDefinition = null; // Set via MEF + + [Export] + [Name(DlrPredefinedClassificationTypeNames.Dot)] + [BaseDefinition(PredefinedClassificationTypeNames.Operator)] + internal static ClassificationTypeDefinition DotClassificationDefinition = null; // Set via MEF + + [Export] + [Name(DlrPredefinedClassificationTypeNames.Comma)] + [BaseDefinition(PredefinedClassificationTypeNames.Operator)] + internal static ClassificationTypeDefinition CommaClassificationDefinition = null; // Set via MEF + + #endregion + + #region IDlrClassifierProvider + + public IClassifier GetClassifier(ITextBuffer buffer) { + IDlrClassifier existing; + if (buffer.Properties.TryGetProperty(typeof(IDlrClassifier), out existing)) { + return existing; + } + + if (_categoryMap == null) { + _categoryMap = FillCategoryMap(_classificationRegistry); + } + var specificContentType = ContentType; + + DlrClassifier res = null; + if (specificContentType == null || buffer.ContentType == specificContentType) { + res = new DlrClassifier(this, Engine, buffer); + } else { + + foreach (var baseType in buffer.ContentType.BaseTypes) { + if (baseType == specificContentType) { + res = new DlrClassifier(this, Engine, buffer); + break; + } + } + } + + if (res != null) { + buffer.Properties.AddProperty(typeof(IDlrClassifier), res); + } + + return res; + } + + public abstract ScriptEngine Engine { + get; + } + + public virtual IContentType ContentType { + get { return null; } + } + + public IClassificationType Comment { + get { return _comment; } + } + + public IClassificationType StringLiteral { + get { return _stringLiteral; } + } + + public IClassificationType Keyword { + get { return _keyword; } + } + + public IClassificationType Operator { + get { return _operator; } + } + + public IClassificationType OpenGroupingClassification { + get { return _openGroupingClassification; } + } + + public IClassificationType CloseGroupingClassification { + get { return _loseGroupingClassification; } + } + + public IClassificationType DotClassification { + get { return _dotClassification; } + } + + public IClassificationType CommaClassification { + get { return _commaClassification; } + } + + #endregion + + internal Dictionary CategoryMap { + get { return _categoryMap; } + } + + private Dictionary FillCategoryMap(IClassificationTypeRegistryService registry) { + var categoryMap = new Dictionary(); + + categoryMap[TokenCategory.DocComment] = _comment = registry.GetClassificationType(PredefinedClassificationTypeNames.Comment); + categoryMap[TokenCategory.LineComment] = registry.GetClassificationType(PredefinedClassificationTypeNames.Comment); + categoryMap[TokenCategory.Comment] = registry.GetClassificationType(PredefinedClassificationTypeNames.Comment); + categoryMap[TokenCategory.NumericLiteral] = registry.GetClassificationType(PredefinedClassificationTypeNames.Literal); + categoryMap[TokenCategory.CharacterLiteral] = registry.GetClassificationType(PredefinedClassificationTypeNames.Character); + categoryMap[TokenCategory.StringLiteral] = _stringLiteral = registry.GetClassificationType(PredefinedClassificationTypeNames.String); + categoryMap[TokenCategory.Keyword] = _keyword = registry.GetClassificationType(PredefinedClassificationTypeNames.Keyword); + categoryMap[TokenCategory.Directive] = registry.GetClassificationType(PredefinedClassificationTypeNames.Keyword); + categoryMap[TokenCategory.Identifier] = registry.GetClassificationType(PredefinedClassificationTypeNames.Identifier); + categoryMap[TokenCategory.Operator] = _operator = registry.GetClassificationType(PredefinedClassificationTypeNames.Operator); + categoryMap[TokenCategory.Delimiter] = registry.GetClassificationType(PredefinedClassificationTypeNames.Operator); + categoryMap[TokenCategory.Grouping] = registry.GetClassificationType(PredefinedClassificationTypeNames.Operator); + categoryMap[TokenCategory.WhiteSpace] = registry.GetClassificationType(PredefinedClassificationTypeNames.WhiteSpace); + categoryMap[TokenCategory.RegularExpressionLiteral] = registry.GetClassificationType(PredefinedClassificationTypeNames.Literal); + _openGroupingClassification = registry.GetClassificationType(DlrPredefinedClassificationTypeNames.OpenGrouping); + _loseGroupingClassification = registry.GetClassificationType(DlrPredefinedClassificationTypeNames.CloseGrouping); + _commaClassification = registry.GetClassificationType(DlrPredefinedClassificationTypeNames.Comma); + _dotClassification = registry.GetClassificationType(DlrPredefinedClassificationTypeNames.Dot); + + return categoryMap; + } + } + + #region Editor Format Definitions + + [Export(typeof(EditorFormatDefinition))] + [ClassificationType(ClassificationTypeNames = DlrPredefinedClassificationTypeNames.OpenGrouping)] + [Name("Open grouping")] + [DisplayName("Open grouping character")] + [UserVisible(true)] + [Order(After = Priority.Default, Before = Priority.High)] + internal sealed class OpenGroupingFormat : ClassificationFormatDefinition { } + + [Export(typeof(EditorFormatDefinition))] + [ClassificationType(ClassificationTypeNames = DlrPredefinedClassificationTypeNames.CloseGrouping)] + [Name("Close grouping")] + [DisplayName("Close grouping character")] + [UserVisible(true)] + [Order(After = Priority.Default, Before = Priority.High)] + internal sealed class CloseGroupingFormat : ClassificationFormatDefinition { } + + [Export(typeof(EditorFormatDefinition))] + [ClassificationType(ClassificationTypeNames = DlrPredefinedClassificationTypeNames.Comma)] + [Name("Comma")] + [DisplayName("Comma character")] + [UserVisible(true)] + [Order(After = Priority.Default, Before = Priority.High)] + internal sealed class CommaFormat : ClassificationFormatDefinition { } + + [Export(typeof(EditorFormatDefinition))] + [ClassificationType(ClassificationTypeNames = DlrPredefinedClassificationTypeNames.Dot)] + [Name("Dot")] + [DisplayName("Dot character")] + [UserVisible(true)] + [Order(After = Priority.Default, Before = Priority.High)] + internal sealed class DotFormat : ClassificationFormatDefinition { } + + #endregion +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/DlrPredefinedClassificationTypeNames.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/DlrPredefinedClassificationTypeNames.cs new file mode 100644 index 0000000000..9861fcbabf --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/DlrPredefinedClassificationTypeNames.cs @@ -0,0 +1,29 @@ +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.IronStudio.Core { + public static class DlrPredefinedClassificationTypeNames { + /// + /// Open grouping classification. Used for (, [, etc... A subtype of the pre-defined + /// operator grouping. + /// + public const string OpenGrouping = "open grouping"; + /// + /// Closed grouping classification. Used for ), ], etc... A subtype of the pre-defined + /// operator grouping. + /// + public const string CloseGrouping = "close grouping"; + + /// + /// Classification used for comma characters when used outside of a literal, comment, etc... + /// + public const string Comma = "comma"; + + /// + /// Classification used for . characters when used outside of a literal, comment, etc... + /// + public const string Dot = "dot"; + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/IDlrClassifier.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/IDlrClassifier.cs new file mode 100644 index 0000000000..0e06b5263b --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/IDlrClassifier.cs @@ -0,0 +1,25 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio.Text.Classification; + + +namespace Microsoft.IronStudio { + public interface IDlrClassifier : IClassifier { + IDlrClassifierProvider Provider { + get; + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/IDlrClassifierProvider.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/IDlrClassifierProvider.cs new file mode 100644 index 0000000000..5a5a2febfa --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/IDlrClassifierProvider.cs @@ -0,0 +1,63 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.Scripting.Hosting; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronStudio { + public interface IDlrClassifierProvider : IClassifierProvider { + + ScriptEngine Engine { + get; + } + + IContentType ContentType { + get; + } + + IClassificationType Comment { + get; + } + + IClassificationType StringLiteral { + get; + } + + IClassificationType Keyword { + get; + } + + IClassificationType Operator { + get; + } + + IClassificationType OpenGroupingClassification { + get; + } + + IClassificationType CloseGroupingClassification { + get; + } + + IClassificationType DotClassification { + get; + } + + IClassificationType CommaClassification { + get; + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/IMixedBuffer.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/IMixedBuffer.cs new file mode 100644 index 0000000000..5a7f53c921 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/IMixedBuffer.cs @@ -0,0 +1,39 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronStudio { + /// + /// IMixedBuffer can be attached as a property to an ITextBuffer. Doing so indicates to tokenization and analysis + /// services that the buffer contains both source code in a specific language as well as non-source code + /// elements. These services can then use this interface so that they only process the language specific elements. + /// + /// Currently the only producer of this interface is the REPL window. + /// + public interface IMixedBuffer { + /// + /// Gets the spans which contain language code + /// + /// + SnapshotSpan[] GetLanguageSpans(ITextSnapshot snapshot); + + /// + /// For a given line and snapshot gets the relevant span on that line which contains source code. + /// + /// If the line does not contain any source code then null is returned. + /// + SnapshotSpan? GetLanguageSpanForLine(ITextSnapshot snapshot, int line); + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/ISnapshotTextContentProvider.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/ISnapshotTextContentProvider.cs new file mode 100644 index 0000000000..157d6ec99c --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/ISnapshotTextContentProvider.cs @@ -0,0 +1,27 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronStudio { + public interface ISnapshotTextContentProvider { + ITextSnapshot Snapshot { + get; + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/AnalysisPriority.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/AnalysisPriority.cs new file mode 100644 index 0000000000..819b4d6828 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/AnalysisPriority.cs @@ -0,0 +1,27 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.IronStudio.Intellisense { + public enum AnalysisPriority { + None, + Low, + Normal, + High, + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/AnalysisQueue.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/AnalysisQueue.cs new file mode 100644 index 0000000000..6fa1882eec --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/AnalysisQueue.cs @@ -0,0 +1,139 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; +using System.Diagnostics; +using Microsoft.IronStudio.Intellisense; + +namespace Microsoft.IronStudio.Library.Intellisense { + /// + /// Provides a single threaded analysis queue. Items can be enqueued into the + /// analysis at various priorities. + /// + public sealed class AnalysisQueue : IDisposable where TAnalyzedType : class { + private readonly Thread _thread; + private readonly AutoResetEvent _event; + private readonly IAnalyzer _analyzer; + private readonly object _queueLock = new object(); + private readonly List[] _queue; + private volatile bool _unload; + private bool _isAnalyzing; + + private const int PriorityCount = (int)AnalysisPriority.High + 1; + + public AnalysisQueue(IAnalyzer analyzer) { + _event = new AutoResetEvent(false); + _analyzer = analyzer; + + _queue = new List[PriorityCount]; + for(int i = 0; i(); + } + + _thread = new Thread(Worker); + _thread.Name = String.Format("Analysis Queue of {0}", typeof(TAnalyzedType).Name); + _thread.Priority = ThreadPriority.BelowNormal; + _thread.IsBackground = true; + _thread.Start(); + } + + public void Enqueue(TAnalyzedType item, AnalysisPriority priority) { + int iPri = (int)priority; + + if (iPri < 0 || iPri > _queue.Length) { + throw new ArgumentException("priority"); + } + + lock (_queueLock) { + // see if we have the item in the queue anywhere... + for (int i = 0; i < _queue.Length; i++) { + if (_queue[i].Remove(item)) { + AnalysisPriority oldPri = (AnalysisPriority)i; + + if (oldPri > priority) { + // if it was at a higher priority then our current + // priority go ahead and raise the new entry to our + // old priority + priority = oldPri; + } + + break; + } + } + + // enqueue the work item + if (priority == AnalysisPriority.High) { + // always try and process high pri items immediately + _queue[iPri].Insert(0, item); + } else { + _queue[iPri].Add(item); + } + _event.Set(); + } + } + + public void Stop() { + if (_thread != null) { + _unload = true; + _event.Set(); + } + } + + public bool IsAnalyzing { + get { + return _isAnalyzing; + } + } + + #region IDisposable Members + + void IDisposable.Dispose() { + Stop(); + } + + #endregion + + private TAnalyzedType GetNextItem() { + for (int i = PriorityCount - 1; i >= 0; i--) { + if (_queue[i].Count > 0) { + var res = _queue[i][0]; + _queue[i].RemoveAt(0); + return res; + } + } + return null; + } + + private void Worker() { + while (!_unload) { + TAnalyzedType workItem; + + lock (_queueLock) { + workItem = GetNextItem(); + } + _isAnalyzing = true; + if (workItem != null) { + _analyzer.Analyze(workItem); + _isAnalyzing = false; + } else { + _event.WaitOne(); + } + } + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/IAnalyzer.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/IAnalyzer.cs new file mode 100644 index 0000000000..4f2029c933 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/IAnalyzer.cs @@ -0,0 +1,24 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.IronStudio.Intellisense { + public interface IAnalyzer { + void Analyze(TAnalyzedType content); + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/IParser.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/IParser.cs new file mode 100644 index 0000000000..9b63d03adb --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/IParser.cs @@ -0,0 +1,28 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using Microsoft.Scripting; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronStudio.Intellisense { + + public interface IParser { + /// + /// Called when the specified content should be parsed. If the content + /// is associated with a text buffer the snap shot being parsed is also + /// provided. + /// + void Parse(TextContentProvider content); + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/ParseQueue.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/ParseQueue.cs new file mode 100644 index 0000000000..b419b6085f --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/ParseQueue.cs @@ -0,0 +1,238 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Threading; +using Microsoft.IronStudio.Core; +using Microsoft.IronStudio.Library; +using Microsoft.Scripting; +using Microsoft.Scripting.Library; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.IronStudio.Intellisense { + /// + /// Provides an asynchronous queue for parsing source code. Multiple items + /// may be parsed simultaneously. Text buffers are monitored for changes and + /// the parser is called when the buffer should be re-parsed. + /// + public class ParseQueue { + private readonly IParser _parser; + private int _analysisPending; + + /// + /// Creates a new parse queue which will parse using the provided parser. + /// + /// + public ParseQueue(IParser parser) { + _parser = parser; + } + + /// + /// Parses the specified text buffer. Continues to monitor the parsed buffer and updates + /// the parse tree asynchronously as the buffer changes. + /// + /// + public void EnqueueBuffer(ITextView textView) { + ITextBuffer buffer = textView.TextBuffer; + var curSnapshot = buffer.CurrentSnapshot; + EnqueWorker(new[] { GetContentProvider(buffer, curSnapshot) }); + + // only attach one parser to each buffer, we can get multiple enqueue's + // for example if a document is already open when loading a project. + if (!buffer.Properties.ContainsProperty(typeof(BufferParser))) { + BufferParser parser = new BufferParser(this, buffer); + buffer.ChangedLowPriority += parser.BufferChangedLowPriority; + + textView.Closed += (sender, args) => { + buffer.ChangedLowPriority -= parser.BufferChangedLowPriority; + buffer.Properties.RemoveProperty(typeof(BufferParser)); + }; + + buffer.Properties.AddProperty(typeof(BufferParser), parser); + } + } + + /// + /// Parses the specified file on disk. + /// + /// + public void EnqueueFile(string filename) { + EnqueWorker(new[] { new FileTextContentProvider(filename) }); + } + + /// + /// Parses the specified list of files on disk. + /// + /// + public void EnqueueFiles(params string[] filenames) { + EnqueueFiles((IEnumerable)filenames); + } + + /// + /// Parses the specified list of files on disk. + /// + public void EnqueueFiles(IEnumerable filenames) { + List providers = new List(); + foreach (var filename in filenames) { + providers.Add(new FileTextContentProvider(filename)); + } + EnqueWorker(providers); + } + + class BufferParser { + private readonly ParseQueue _parseQueue; + private readonly Timer _timer; + private readonly ITextBuffer _buffer; + private ITextSnapshot _snapshot; + + private bool _parsing, _requeue, _textChange; + + private const int ReparseDelay = 1000; // delay in MS before we re-parse a buffer w/ non-line changes. + + public BufferParser(ParseQueue queue, ITextBuffer buffer) { + _parseQueue = queue; + _timer = new Timer(ReparseWorker, null, Timeout.Infinite, Timeout.Infinite); + _buffer = buffer; + } + + internal void ReparseWorker(object unused) { + ITextSnapshot curSnapshot; + lock (this) { + if (_parsing) { + return; + } + + _parsing = true; + curSnapshot = _snapshot; + } + + _parseQueue.DoParse(new[] { GetContentProvider(_buffer, _snapshot) }); + + lock (this) { + _parsing = false; + if (_requeue) { + ThreadPool.QueueUserWorkItem(ReparseWorker); + } + _requeue = false; + } + } + + internal void BufferChangedLowPriority(object sender, TextContentChangedEventArgs e) { + lock (this) { + // only immediately re-parse on line changes after we've seen a text change. + _snapshot = e.After; + + if (_parsing) { + // we are currently parsing, just reque when we complete + _requeue = true; + _timer.Change(Timeout.Infinite, Timeout.Infinite); + } else if (LineAndTextChanges(e)) { + // user pressed enter, we should reque immediately + ThreadPool.QueueUserWorkItem(ReparseWorker); + _timer.Change(Timeout.Infinite, Timeout.Infinite); + } else { + // parse if the user doesn't do anything for a while. + _textChange = IncludesTextChanges(e); + _timer.Change(ReparseDelay, Timeout.Infinite); + } + } + } + + /// + /// Used to track if we have line + text changes, just text changes, or just line changes. + /// + /// If we have text changes followed by a line change we want to immediately reparse. + /// If we have just text changes we want to reparse in ReparseDelay ms from the last change. + /// If we have just repeated line changes (e.g. someone's holding down enter) we don't want to + /// repeatedly reparse, instead we want to wait ReparseDelay ms. + /// + private bool LineAndTextChanges(TextContentChangedEventArgs e) { + if (_textChange) { + _textChange = false; + return e.Changes.IncludesLineChanges; + } + + bool mixedChanges = false; + if (e.Changes.IncludesLineChanges) { + mixedChanges = IncludesTextChanges(e); + } + + return mixedChanges; + } + + /// + /// Returns true if the change incldues text changes (not just line changes). + /// + private static bool IncludesTextChanges(TextContentChangedEventArgs e) { + bool mixedChanges = false; + foreach (var change in e.Changes) { + if (change.OldText != "" || change.NewText != Environment.NewLine) { + mixedChanges = true; + break; + } + } + return mixedChanges; + } + } + + private void EnqueWorker(IEnumerable contents) { + Interlocked.Increment(ref _analysisPending); + + ThreadPool.QueueUserWorkItem( + dummy => { + DoParse(contents); + } + ); + } + + private void DoParse(IEnumerable contents) { + try { + foreach (var content in contents) { + _parser.Parse(content); + } + } finally { + Interlocked.Decrement(ref _analysisPending); + } + } + + private static SnapshotSpan[] GetSpansToAnalyze(ITextBuffer buffer, ITextSnapshot snapshot) { + if (buffer.Properties.ContainsProperty(typeof(IMixedBuffer))) { + var mixedBuffer = buffer.Properties.GetProperty(typeof(IMixedBuffer)); + return mixedBuffer.GetLanguageSpans(snapshot); + } + return null; + } + + private static TextContentProvider GetContentProvider(ITextBuffer buffer, ITextSnapshot snapshot) { + var spans = GetSpansToAnalyze(buffer, snapshot); + if (spans == null) { + return new SnapshotTextContentProvider(snapshot); + } else if (spans.Length == 1) { + return new SnapshotSpanTextContentProvider(spans[0]); + } else if (spans.Length == 0) { + return new SnapshotSpanTextContentProvider(new SnapshotSpan(snapshot, new Span(0, 0))); + } else { + return new SnapshotMultipleSpanTextContentProvider(new NormalizedSnapshotSpanCollection(spans)); + } + } + + public bool IsParsing { + get { + return _analysisPending > 0; + } + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/XamlAnalysis.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/XamlAnalysis.cs new file mode 100644 index 0000000000..fd94d7a616 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Intellisense/XamlAnalysis.cs @@ -0,0 +1,158 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.IO; +using System.Xaml; + +namespace Microsoft.IronStudio.Intellisense { + /// + /// Walks the XAML file and provides data based upon things which should be provided via intellisense. + /// + public sealed class XamlAnalysis { + private readonly Dictionary _knownTypes = new Dictionary(); + private readonly Dictionary _eventInfo = new Dictionary(); + + enum MemberType { + Unknown, + XName, + Event + } + + public XamlAnalysis(string filename) { + try { + var settings = new XamlXmlReaderSettings(); + settings.ProvideLineInfo = true; + + using (XamlXmlReader reader = new XamlXmlReader(filename, settings)) { + Analyze(reader); + } + } catch { + // ignore malformed XAML - XamlReader does a bad job of documenting what it throws + // so we just need to try/catch here. + } + } + + public XamlAnalysis(TextReader textReader) { + try { + var settings = new XamlXmlReaderSettings(); + settings.ProvideLineInfo = true; + + using (XamlXmlReader reader = new XamlXmlReader(textReader, settings)) { + Analyze(reader); + } + } catch { + // ignore malformed XAML - XamlReader does a bad job of documenting what it throws + // so we just need to try/catch here. + } + } + + private void Analyze(XamlXmlReader reader) { + Stack objectTypes = new Stack(); + Stack nameStack = new Stack(); + Stack eventStack = new Stack(); + while (reader.Read()) { + switch (reader.NodeType) { + case XamlNodeType.StartObject: objectTypes.Push(reader.Type); break; + case XamlNodeType.EndObject: + if (objectTypes.Count > 0) { + objectTypes.Pop(); + } + break; + case XamlNodeType.NamespaceDeclaration: + break; + case XamlNodeType.StartMember: + var property = reader.Member; + if (property.Name == "Name" && property.Type.UnderlyingType == typeof(string)) { + nameStack.Push(MemberType.XName); + } else if (property.IsEvent) { + nameStack.Push(MemberType.Event); + eventStack.Push(property); + } else { + nameStack.Push(MemberType.Unknown); + } + break; + + case XamlNodeType.EndMember: + if (nameStack.Pop() == MemberType.Event) { + eventStack.Pop(); + } + break; + case XamlNodeType.GetObject: + + case XamlNodeType.Value: + object value = reader.Value; + if (value is string) { + switch (nameStack.Peek()) { + case MemberType.XName: + // we are writing a x:Name, save it so we can later get the name from the scope + _knownTypes[(string)value] = new XamlTypeReference(objectTypes.Peek(), reader.LineNumber, reader.LinePosition); + break; + case MemberType.Event: + // we have an event handler, save the method name and the XamlMember for the event + _eventInfo[(string)value] = new XamlMemberReference(eventStack.Peek(), reader.LineNumber, reader.LinePosition); + break; + } + } + + break; + } + } + } + + /// + /// Dictionary from object name to type information for that object. + /// + public Dictionary NamedObjects { + get { + return _knownTypes; + } + } + + /// + /// Dictionary from event handler name to event handler member info. The name referes + /// to something which will be fetched from the root object for binding the event + /// handler to. + /// + public Dictionary EventHandlers { + get { + return _eventInfo; + } + } + } + + public struct XamlMemberReference { + public readonly XamlMember Member; + public readonly int LineNumber; + public readonly int LineOffset; + + public XamlMemberReference(XamlMember member, int lineNo, int lineOffset) { + Member = member; + LineNumber = lineNo; + LineOffset = lineOffset; + } + } + + public struct XamlTypeReference { + public readonly XamlType Type; + public readonly int LineNumber; + public readonly int LineOffset; + + public XamlTypeReference(XamlType type, int lineNo, int lineOffset) { + Type = type; + LineNumber = lineNo; + LineOffset = lineOffset; + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Navigation/IScopeNode.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Navigation/IScopeNode.cs new file mode 100644 index 0000000000..c59974a82b --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Navigation/IScopeNode.cs @@ -0,0 +1,44 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; +using System.Globalization; +using Microsoft.Scripting; + +namespace Microsoft.IronStudio.Navigation { + public interface IScopeNode { + bool IsFunction { + get; + } + + string Name { + get; + } + + string Description { + get; + } + + SourceLocation Start { + get; + } + SourceLocation End { + get; + } + + IEnumerable NestedScopes { + get; + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/RemoteEvaluation/AsyncAccess.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/RemoteEvaluation/AsyncAccess.cs new file mode 100644 index 0000000000..4501c68b92 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/RemoteEvaluation/AsyncAccess.cs @@ -0,0 +1,43 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.Remoting; + +namespace Microsoft.IronStudio.RemoteEvaluation { + /// + /// Special MarshalByRefObject which is used to communicate we need to abort + /// the current work item on a 2ndary async communication channel. + /// + class AsyncAccess : MarshalByRefObject { + private readonly RemoteProxy _proxy; + + public AsyncAccess(RemoteProxy proxy) { + _proxy = proxy; + } + + public void Abort() { + _proxy.Abort(); + } + + public ObjectHandle CommandDispatcher { + get { + return _proxy.CommandDispatcher; + } + set { + _proxy.CommandDispatcher = value; + } + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/RemoteEvaluation/ExecutionQueue.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/RemoteEvaluation/ExecutionQueue.cs new file mode 100644 index 0000000000..38524cb832 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/RemoteEvaluation/ExecutionQueue.cs @@ -0,0 +1,126 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Runtime.Remoting; +using System.Threading; + +namespace Microsoft.IronStudio.RemoteEvaluation { + class ExecutionQueue { + private readonly Thread _executionThread; + private bool _shouldShutDown; + private AutoResetEvent _event = new AutoResetEvent(false); + private List _items = new List(); + private bool _running, _aborting; + private Action _commandDispatcher; + + public ExecutionQueue(ApartmentState state) { + _executionThread = new Thread(ExecutionThread); + _executionThread.IsBackground = true; + _executionThread.Name = "ExecutionThread"; + _executionThread.SetApartmentState(state); + _executionThread.Start(); + _commandDispatcher = (action) => action(); + } + + private void ExecutionThread() { + while (!_shouldShutDown) { + ExecutionQueueItem curItem = null; + lock (_items) { + if (_items.Count > 0) { + curItem = _items[0]; + _items.RemoveAt(0); + } + } + + if (curItem != null) { + try { + _running = true; + try { + _commandDispatcher(() => curItem.Process()); + } finally { + _running = false; + } + + while (_aborting) { + // wait for abort thread to complete + ; + } + } catch (ThreadAbortException) { + Thread.ResetAbort(); + } + curItem.Complete(); + } else { + _event.WaitOne(); + } + } + } + + public void Process(ExecutionQueueItem item) { + if (Running) { + // Remote side called us, we called back, and now they're calling back again. Process this request synchronously so we don't hang + _commandDispatcher(() => { + item.Process(); + }); + } else { + Enqueue(item); + item.Wait(); + } + } + + public ObjectHandle CommandDispatcher { + get { + return new ObjectHandle(_commandDispatcher); + } + set { + _commandDispatcher = (Action)value.Unwrap(); + } + } + + public bool Running { + get { + return _running; + } + } + + public void Shutdown() { + lock (_items) { + _items.Clear(); + } + + _shouldShutDown = true; + _event.Set(); + } + + public void Abort() { + lock (_items) { + _items.Clear(); + } + + _aborting = true; + if (_running) { + _executionThread.Abort(); + } + _aborting = false; + } + + public void Enqueue(ExecutionQueueItem item) { + lock (_items) { + _items.Add(item); + _event.Set(); + } + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/RemoteEvaluation/ExecutionQueueItem.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/RemoteEvaluation/ExecutionQueueItem.cs new file mode 100644 index 0000000000..d6a1c99cd8 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/RemoteEvaluation/ExecutionQueueItem.cs @@ -0,0 +1,35 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Threading; + +namespace Microsoft.IronStudio.RemoteEvaluation { + abstract class ExecutionQueueItem { + private readonly AutoResetEvent _event = new AutoResetEvent(false); + + public abstract void Process(); + + public void Wait() { + _event.WaitOne(); + } + + internal void Complete() { + _event.Set(); + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/RemoteEvaluation/RemoteProxy.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/RemoteEvaluation/RemoteProxy.cs new file mode 100644 index 0000000000..2c1415645d --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/RemoteEvaluation/RemoteProxy.cs @@ -0,0 +1,303 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Collections.Generic; +using System.Diagnostics; +using System.IO; +using System.Runtime.Remoting; +using System.Runtime.Remoting.Channels; +using System.Runtime.Remoting.Channels.Ipc; +using System.Runtime.Remoting.Lifetime; +using System.Runtime.Remoting.Messaging; +using System.Runtime.Serialization.Formatters; +using System.Threading; +using Microsoft.Scripting.Hosting; +using Microsoft.Win32.SafeHandles; + +namespace Microsoft.IronStudio.RemoteEvaluation { + class RemoteProxy : MarshalByRefObject { + private AutoResetEvent _processEvent = new AutoResetEvent(false); + private AutoResetEvent _shutdownEvent = new AutoResetEvent(false); + private WaitHandle _processHandle; + private AsyncAccess _abort; + + private static ExecutionQueue _queue; + + public RemoteProxy(ApartmentState state) { + _abort = new AsyncAccess(this); + _queue = new ExecutionQueue(state); + } + + public ScriptRuntime CreateRuntime(ScriptRuntimeSetup setup) { + return new ScriptRuntime(setup); + } + + public void Shutdown() { + _queue.Shutdown(); + _shutdownEvent.Set(); + } + + public ObjectHandle CommandDispatcher { + get { + return _queue.CommandDispatcher; + } + set { + _queue.CommandDispatcher = value; + } + } + + /// + /// Stops execution of the current work item in the queue and flushes the queue. + /// + internal void Abort() { + _queue.Abort(); + } + + public void SetConsoleOut(TextWriter writer) { + Console.SetOut(writer); + } + + public void SetConsoleError(TextWriter writer) { + Console.SetError(writer); + } + + public void SetConsoleIn(TextReader reader) { + Console.SetIn(reader); + } + + public void SetParentProcess(int id) { + var process = Process.GetProcessById(id); + _processHandle = new ProcessWaitHandle(process.Handle); + _processEvent.Set(); + } + + class ProcessWaitHandle : WaitHandle { + public ProcessWaitHandle(IntPtr handle) { + SafeWaitHandle = new SafeWaitHandle(handle, false); + } + } + + public override object InitializeLifetimeService() { + return null; + } + + public void Nop() { + } + + #region Channel Creation + + [System.Diagnostics.CodeAnalysis.SuppressMessage("Microsoft.Security", "CA2116:AptcaMethodsShouldOnlyCallAptcaMethods")] // TODO: Microsoft.Scripting does not need to be APTCA + internal static IpcChannel CreateChannel(string channelName, string portName, IServerFormatterSinkProvider sinkProvider) { + // The Hosting API classes require TypeFilterLevel.Full to be remoted + var serverProv = sinkProvider; + System.Collections.IDictionary properties = new Hashtable(); + properties["name"] = channelName; + properties["portName"] = portName; + properties["includeVersions"] = false; + // exclusiveAddressUse corresponds to the FILE_FLAG_FIRST_PIPE_INSTANCE flag of CreateNamedPipe. + // Setting it to true seems to cause "Failed to create an IPC Port: Access is denied." occasionally. + // TODO: Setting this to false is secure only if we use ACLs as well. + properties["exclusiveAddressUse"] = false; + + // Create the channel. + return new IpcChannel(properties, null, serverProv); + } + + /// + /// Publish objects so that the host can use it, and then block indefinitely (until the input stream is open). + /// + /// Note that we should publish only one object, and then have other objects be accessible from it. Publishing + /// multiple objects can cause problems if the client does a call like "remoteProxy1(remoteProxy2)" as remoting + /// will not be able to know if the server object for both the proxies is on the same server. + /// + /// The IPC channel that the remote console expects to use to communicate with the ScriptEngine + internal void StartServer() { + string remoteRuntimeChannelName = Guid.NewGuid().ToString(); + + Debug.Assert(ChannelServices.GetChannel(remoteRuntimeChannelName) == null); + + LifetimeServices.LeaseTime = GetSevenDays(); + LifetimeServices.LeaseManagerPollTime = GetSevenDays(); + LifetimeServices.RenewOnCallTime = GetSevenDays(); + LifetimeServices.SponsorshipTimeout = GetSevenDays(); + + // create two channels, one for synchronous execution of user code, one for async requests to abort long-running user code. + + IpcChannel channel = RegisterIpcChannel("ipc", remoteRuntimeChannelName, new SinkProvider()); + var provider = new BinaryServerFormatterSinkProvider(); + provider.TypeFilterLevel = TypeFilterLevel.Full; + IpcChannel abortChannel = RegisterIpcChannel("ipcabort", remoteRuntimeChannelName + "ABORT", provider); + + var objRef = RemotingServices.Marshal(this, "RemoteProxy"); + var abortObjRef = RemotingServices.Marshal(_abort, "RemoteAbort"); + try { + //RemoteCommandDispatcher remoteCommandDispatcher = new RemoteCommandDispatcher(scope); + //RemotingServices.Marshal(remoteCommandDispatcher, CommandDispatcherUri); + + // Let the remote console know that the startup output (if any) is complete. We use this instead of + // a named event as we want all the startup output to reach the remote console before it proceeds. + foreach (var x in channel.GetUrlsForUri(objRef.URI)) { + Console.WriteLine("URI: {0}", x); + } + + foreach (var x in abortChannel.GetUrlsForUri(abortObjRef.URI)) { + Console.WriteLine("ABORTURI: {0}", x); + } + + // wait to get our parent process... + _processEvent.WaitOne(); + + // now wait for our parent to exit or for them to signal we should shutdown + switch (WaitHandle.WaitAny(new[] { _shutdownEvent, _processHandle })) { + case 1: + // rip the process as quickly as possible when our parent unexpected dies. + Environment.FailFast("parent process died unexpectedly"); + break; + } + } finally { + ChannelServices.UnregisterChannel(channel); + } + } + + internal static IpcChannel RegisterIpcChannel(string name, string remoteRuntimeChannelName, IServerFormatterSinkProvider sinkProvider) { + IpcChannel channel = CreateChannel(name, remoteRuntimeChannelName, sinkProvider); + + ChannelServices.RegisterChannel(channel, false); + return channel; + } + + private static TimeSpan GetSevenDays() { + return new TimeSpan(7, 0, 0, 0); // days,hours,mins,secs + } + + #endregion + + #region Remoting Sinks + + /// + /// Provides a SinkProvider whos sink will delegate to the binary sink but will marshal + /// all requests onto a well known thread. + /// + class SinkProvider : IServerFormatterSinkProvider, IServerChannelSinkProvider { + private readonly BinaryServerFormatterSinkProvider _provider = new BinaryServerFormatterSinkProvider( + new Dictionary { + { "includeVersions", false } + }, + null + ); + + public SinkProvider() { + _provider.TypeFilterLevel = TypeFilterLevel.Full; + } + + #region IServerChannelSinkProvider Members + + public IServerChannelSink CreateSink(IChannelReceiver channel) { + return new ServerChannelSink(_provider.CreateSink(channel)); + } + + public void GetChannelData(IChannelDataStore channelData) { + _provider.GetChannelData(channelData); + } + + public IServerChannelSinkProvider Next { + get { + return _provider.Next; + } + set { + _provider.Next = value; + } + } + + #endregion + } + + class ServerChannelSink : IServerChannelSink { + private IServerChannelSink _iServerChannelSink; + + public ServerChannelSink(IServerChannelSink iServerChannelSink) { + _iServerChannelSink = iServerChannelSink; + } + + #region IServerChannelSink Members + + public void AsyncProcessResponse(IServerResponseChannelSinkStack sinkStack, object state, IMessage msg, ITransportHeaders headers, System.IO.Stream stream) { + _iServerChannelSink.AsyncProcessResponse(sinkStack, state, msg, headers, stream); + } + + public Stream GetResponseStream(IServerResponseChannelSinkStack sinkStack, object state, IMessage msg, ITransportHeaders headers) { + return GetResponseStream(sinkStack, state, msg, headers); + } + + public IServerChannelSink NextChannelSink { + get { return _iServerChannelSink.NextChannelSink; } + } + + public ServerProcessing ProcessMessage(IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream requestStream, out IMessage responseMsg, out ITransportHeaders responseHeaders, out Stream responseStream) { + // marshal the request onto our dedicated thread for processing requests + var msgInfo = new MessageInfo(_iServerChannelSink, sinkStack, requestMsg, requestHeaders, requestStream); + _queue.Process(msgInfo); + + responseMsg = msgInfo.ResponseMsg; + responseHeaders = msgInfo.ResponseHeaders; + responseStream = msgInfo.ResponseStream; + + return msgInfo.ServerProcessing; + } + + #endregion + + #region IChannelSinkBase Members + + public System.Collections.IDictionary Properties { + get { return _iServerChannelSink.Properties; } + } + + #endregion + } + + class MessageInfo : ExecutionQueueItem { + public readonly IServerChannelSink _serverSink; + public readonly IServerChannelSinkStack _sinkStack; + public readonly IMessage _requestMsg; + public readonly ITransportHeaders _requestHeaders; + public readonly Stream _requestStream; + + public IMessage ResponseMsg; + public ITransportHeaders ResponseHeaders; + public Stream ResponseStream; + public ServerProcessing ServerProcessing; + + public MessageInfo(IServerChannelSink serverSink, IServerChannelSinkStack sinkStack, IMessage requestMsg, ITransportHeaders requestHeaders, Stream stream) { + _serverSink = serverSink; + _sinkStack = sinkStack; + _requestMsg = requestMsg; + _requestHeaders = requestHeaders; + _requestStream = stream; + } + + public override void Process() { + ServerProcessing = _serverSink.ProcessMessage(_sinkStack, _requestMsg, _requestHeaders, _requestStream, out ResponseMsg, out ResponseHeaders, out ResponseStream); + } + } + + #endregion + + public void SetCurrentDirectory(string dir) { + Environment.CurrentDirectory = dir; + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/RemoteEvaluation/RemoteScriptFactory.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/RemoteEvaluation/RemoteScriptFactory.cs new file mode 100644 index 0000000000..6e0318f187 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/RemoteEvaluation/RemoteScriptFactory.cs @@ -0,0 +1,253 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections; +using System.Diagnostics; +using System.IO; +using System.Runtime.InteropServices; +using System.Runtime.Remoting; +using System.Runtime.Remoting.Channels; +using System.Runtime.Remoting.Channels.Ipc; +using System.Runtime.Serialization.Formatters; +using System.Threading; +using Microsoft.Scripting.Hosting; + +namespace Microsoft.IronStudio.RemoteEvaluation { + /// + /// Provides creation of a remote ScriptRuntime for DLR based languages. + /// + /// Creating an instance of the class will cause a new process to be started. ScriptRuntimes + /// can then be created in that new process by calling the CreateRuntime function. + /// + public sealed class RemoteScriptFactory : IDisposable { + private readonly Process _remoteRuntimeProcess; + private readonly AutoResetEvent _remoteOutputReceived = new AutoResetEvent(false); + private readonly RemoteProxy _proxy; + private readonly AsyncAccess _asyncAccess; + + private static bool _registeredChannel; + private static object _lock = new object(); + + /// + /// Creates a new RemoteScriptFactory. A remote process will be created for + /// execution of code. The code will execute on a thread with the specified + /// apartment state. + /// + public RemoteScriptFactory(ApartmentState aptState) { + Process process = null; + try { + RegisterChannel(); + + process = new Process(); + process.StartInfo = GetProcessStartInfo(aptState); + + _remoteRuntimeProcess = process; + + if (!process.Start() || process.HasExited) { + throw new InvalidOperationException(String.Format("Failed to start remote REPL process: {0}", process.ExitCode)); + } + + // read in the channel names we will connect to. There are two of them, one is + // bound to a certain thread, the 2nd is async and enables aborting work items. + string uri = process.StandardOutput.ReadLine(); + if (!uri.StartsWith("URI: ")) { + throw new InvalidOperationException("Didn't get URI, got " + uri); + } + + string abortUri = process.StandardOutput.ReadLine(); + if (!abortUri.StartsWith("ABORTURI: ")) { + throw new InvalidOperationException("Didn't get ABORTURI, got " + abortUri); + } + + // finally get our objects + _proxy = (RemoteProxy)RemotingServices.Connect(typeof(RemoteProxy), uri.Substring(5)); + _proxy.SetParentProcess(Process.GetCurrentProcess().Id); + + _asyncAccess = (AsyncAccess)RemotingServices.Connect(typeof(AsyncAccess), abortUri.Substring(10)); + } finally { + if (_proxy == null) { + if (process != null) { + TerminateProcess(process.Handle, 1); + process.Close(); + } + GC.SuppressFinalize(this); + } + } + } + + private static void RegisterChannel() { + lock (_lock) { + if (!_registeredChannel) { + var provider = new BinaryServerFormatterSinkProvider(); + provider.TypeFilterLevel = TypeFilterLevel.Full; + var properties = new Hashtable(); + properties["name"] = Guid.NewGuid().ToString(); + properties["portName"] = Guid.NewGuid().ToString(); + + IpcChannel channel = new IpcChannel(properties, null, provider); + // Register a channel so that we can get communication back from the other side. + System.Runtime.Remoting.Channels.ChannelServices.RegisterChannel(channel, false); + _registeredChannel = true; + } + } + } + + ~RemoteScriptFactory() { + Dispose(); + } + + #region Public APIs + + public static void RunServer(ApartmentState state) { + new RemoteProxy(state).StartServer(); + } + + /// + /// Creates a ScriptRuntime in the remote process. + /// + public ScriptRuntime CreateRuntime(ScriptRuntimeSetup setup) { + return _proxy.CreateRuntime(setup); + } + + /// + /// Shuts down the remote process. + /// + public void Shutdown() { + try { + _proxy.Shutdown(); + } catch { + } + } + + /// + /// Aborts all work in the remote process. The current work item, if any, + /// is aborted with a Thread.Abort. All queued work items will be cleared + /// and will not be executed. + /// + public void Abort() { + try { + _asyncAccess.Abort(); + } catch (RemotingException) { + } + } + + + public ObjectHandle CommandDispatcher { + get { + return _asyncAccess.CommandDispatcher; + } + set { + _asyncAccess.CommandDispatcher = value; + } + } + /// + /// Sets the TextWriter that is used for output in the remote process. + /// + public void SetConsoleOut(TextWriter writer) { + _proxy.SetConsoleOut(writer); + } + + /// + /// Sets the TextReader that is used for input in the remote process. + /// + public void SetConsoleIn(TextReader reader) { + _proxy.SetConsoleIn(reader); + } + + /// + /// Sets the TextWriter that is used for error output in the remote process. + /// + public void SetConsoleError(TextWriter writer) { + _proxy.SetConsoleError(writer); + } + + /// + /// Disposes of the RemoteScriptFactory and closes the remote process. + /// + public void Dispose() { + if (_proxy != null) { + if (!_remoteRuntimeProcess.HasExited) { + try { + // shutting down can throw if the process successfully + // exits while we're in the Shutdown call + _proxy.Shutdown(); + } catch (RemotingException) { + } + } + + if (!_remoteRuntimeProcess.HasExited) { + TerminateProcess(_remoteRuntimeProcess.Handle, 1); + } + } + GC.SuppressFinalize(this); + } + + public bool IsDisconnected { + get{ + try { + _proxy.Nop(); + } catch { + return true; + } + return false; + } + } + #endregion + + #region Internal Implementation Details + + [DllImport("kernel32.dll", SetLastError = true)] + static extern int TerminateProcess(IntPtr processIdOrHandle, uint exitCode); + + private ProcessStartInfo GetProcessStartInfo(ApartmentState state) { + const string exeName = "RemoteScriptFactory.exe"; + + // The exe is located in the IronStudio install dir. The repl can't run though unless IronPython and IronRuby dlls are GAC'd. + string processBasePath = Path.GetDirectoryName(typeof(RemoteScriptFactory).Assembly.Location); + +#if DEBUG + // While developing the tooling use an exe located in Bin\Debug as we don't GAC any dlls. + string devBinPath = Environment.GetEnvironmentVariable("DLR_ROOT"); + if (devBinPath != null) { + devBinPath = Path.Combine(devBinPath, @"Bin\Debug"); + if (File.Exists(Path.Combine(devBinPath, exeName))) { + processBasePath = devBinPath; + } + } +#endif + + ProcessStartInfo processInfo = new ProcessStartInfo(); + processInfo.FileName = Path.Combine(processBasePath, exeName); + processInfo.CreateNoWindow = true; + + // Set UseShellExecute to false to enable redirection. + processInfo.UseShellExecute = false; + + // Redirect the standard streams. The output streams will be read asynchronously using an event handler. + processInfo.RedirectStandardError = true; + processInfo.RedirectStandardOutput = true; + processInfo.RedirectStandardInput = true; + processInfo.Arguments = state.ToString(); + + return processInfo; + } + + #endregion + + public void SetCurrentDirectory(string dir) { + _proxy.SetCurrentDirectory(dir); + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/AutoIndent.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/AutoIndent.cs new file mode 100644 index 0000000000..9afa4cd482 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/AutoIndent.cs @@ -0,0 +1,181 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; + +using Microsoft.IronStudio.Library.Repl; + +using Microsoft.VisualStudio.Language.StandardClassification; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods; + +namespace Microsoft.IronStudio.Core.Repl { + public static class AutoIndent { + private static string _groupingChars = ",([{"; + + private static int GetIndentation(string line, int tabSize) { + int res = 0; + for (int i = 0; i < line.Length; i++) { + if (line[i] == ' ') { + res++; + } else if (line[i] == '\t') { + res += tabSize; + } else { + break; + } + } + return res; + } + + private static bool StartsGrouping(ClassificationSpan token) { + return token.ClassificationType.IsOfType("OpenGroupingClassification"); + } + + private static string CurrentLine(IWpfTextView buffer) { + return buffer.TextSnapshot.GetLineFromPosition(buffer.Caret.Position.BufferPosition.Position).GetText(); + } + + private static string CurrentLine(ReplWindow buffer) { + return CurrentLine(buffer.CurrentView); + } + + private static bool EndsGrouping(ClassificationSpan token) { + return token.ClassificationType.IsOfType("CloseGroupingClassification"); + } + + private static bool IsGroupingChar(char c) { + return _groupingChars.IndexOf(c) >= 0; + } + + private static int CalculateIndentation(string baseline, ITextSnapshotLine line, IWpfTextView view, IClassifier classifier) { + int indentation = GetIndentation(baseline, view.Options.GetTabSize()); + var sline = baseline.Trim(); + int tabSize = view.Options.GetIndentSize(); + var tokens = classifier.GetClassificationSpans(line.Extent); + if (tokens.Count > 0) { + if (!tokens[tokens.Count - 1].ClassificationType.IsOfType(PredefinedClassificationTypeNames.Comment)) { + var lastChar = sline.Length == 0 ? '\0' : sline[sline.Length - 1]; + if (lastChar == ':') { + indentation += tabSize; + } else if (IsGroupingChar(lastChar)) { + if (tokens != null) { + var groupings = new Stack(); + foreach (var token in tokens) { + if (token.Span.Start.Position > view.Caret.Position.BufferPosition.Position) { + break; + } + if (StartsGrouping(token)) { + groupings.Push(token); + } else if (groupings.Count > 0 && EndsGrouping(token)) { + groupings.Pop(); + } + } + if (groupings.Count > 0) { + indentation = groupings.Peek().Span.End.Position - line.Extent.Start.Position; + } + } + } else if (indentation >= tabSize) { + if (tokens.Count > 0 && tokens[0].ClassificationType.Classification == PredefinedClassificationTypeNames.Keyword && tokens[0].Span.GetText() == "return") { + indentation -= tabSize; + } + } + } + } + return indentation; + } + + private static bool IsCaretInStringLiteral(ReplWindow buffer) { + var caret = buffer.CurrentView.Caret; + var spans = buffer.Classifier.GetClassificationSpans(buffer.CurrentView.GetTextElementSpan(caret.Position.BufferPosition)); + if (spans.Count > 0) { + return spans[0].ClassificationType.IsOfType(PredefinedClassificationTypeNames.String); + } + return false; + } + + private static bool IsExtendedLine(string line) { + var sline = line.Trim(); + if (sline.Length == 0) { + return false; + } + var lastChar = sline[sline.Length - 1]; + return IsGroupingChar(lastChar) || lastChar == '\\'; + } + + internal static void HandleReturn(ReplWindow buffer) { + HandleReturn(buffer.CurrentView, buffer.Classifier); + } + + public static void HandleReturn(IWpfTextView view, IClassifier classifier) { + int curLine = view.Caret.Position.BufferPosition.GetContainingLine().LineNumber; + int startLine = curLine; + + // skip blank lines as far as calculating indentation goes... + bool hasNonWhiteSpace = false; + string lineText; + ITextSnapshotLine line; + do { + line = view.TextSnapshot.GetLineFromLineNumber(curLine); + if (curLine == startLine) { + // if we're in the middle of a line only consider text to the left for white space detection + lineText = line.GetText().Substring(0, view.Caret.Position.BufferPosition.Position - view.Caret.Position.BufferPosition.GetContainingLine().Start); + } else { + lineText = line.GetText(); + } + foreach (char c in lineText) { + if (!Char.IsWhiteSpace(c)) { + hasNonWhiteSpace = true; + break; + } + } + if (!hasNonWhiteSpace) { + curLine--; + } + } while (!hasNonWhiteSpace && curLine > 0); + + int indentation = CalculateIndentation(lineText, line, view, classifier); + if (curLine != startLine) { + // enter on a blank line, don't re-indent instead just maintain the current indentation + indentation = Math.Min(indentation, (view.Caret.Position.BufferPosition.Position - view.Caret.ContainingTextViewLine.Start.Position)); + } + using (var edit = view.TextBuffer.CreateEdit()) { + if (view.Selection.IsActive) { + foreach (var span in view.Selection.SelectedSpans) { + edit.Delete(span); + } + } + edit.Insert(view.Caret.Position.BufferPosition.Position, view.Options.GetNewLineCharacter()); + if (view.Options.IsConvertTabsToSpacesEnabled()) { + edit.Insert(view.Caret.Position.BufferPosition.Position, new String(' ', indentation)); + } else { + edit.Insert(view.Caret.Position.BufferPosition.Position, new String('\t', indentation / view.Options.GetTabSize())); + } + edit.Apply(); + } + + view.Caret.EnsureVisible(); + } + + private static string GetPreviousLine(IWpfTextView buffer) { + int lineno = buffer.Caret.Position.BufferPosition.GetContainingLine().LineNumber; + if (lineno < 1) { + return String.Empty; + } + return buffer.TextSnapshot.GetLineFromLineNumber(lineno - 1).GetText(); + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/CancelExecutionCommand.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/CancelExecutionCommand.cs new file mode 100644 index 0000000000..29be460ab4 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/CancelExecutionCommand.cs @@ -0,0 +1,60 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.IronStudio.Repl; +using System.ComponentModel.Composition; +using System.IO; +using System.Windows.Threading; +using System.Reflection; +using System.Windows.Controls; +using System.Windows.Media.Imaging; +using System.Windows.Media; + +namespace Microsoft.IronStudio.Core.Repl { + [Export(typeof(IReplCommand))] + class CancelExecutionCommand : IReplCommand { + #region IReplCommand Members + + public void Execute(IReplWindow window, string arguments) { + window.AbortCommand(); + } + + public string Description { + get { return "Stops execution of the current command."; } + } + + public string Command { + get { return null; } + } + + public object ButtonContent { + get { + var image = new BitmapImage(); + image.BeginInit(); + image.StreamSource = Assembly.GetExecutingAssembly().GetManifestResourceStream("Microsoft.IronStudio.Core.Resources.CancelEvaluation.gif"); + image.EndInit(); + var res = new Image(); + res.Source = image; + res.Width = res.Height = 16; + return res; + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/ClearScreenReplCommand.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/ClearScreenReplCommand.cs new file mode 100644 index 0000000000..f979a1261f --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/ClearScreenReplCommand.cs @@ -0,0 +1,56 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel.Composition; +using System.Reflection; +using System.Windows.Controls; +using System.Windows.Media.Imaging; +using Microsoft.IronStudio.Repl; + +namespace Microsoft.IronStudio.Core.Repl { + [Export(typeof(IReplCommand))] + class ClearScreenReplCommand : IReplCommand { + #region IReplCommand Members + + public void Execute(IReplWindow window, string arguments) { + window.ClearScreen(); + } + + public string Description { + get { return "Clears the contents of the REPL editor window"; } + } + + public string Command { + get { return "cls"; } + } + + public object ButtonContent { + get { + var image = new BitmapImage(); + image.BeginInit(); + image.StreamSource = Assembly.GetExecutingAssembly().GetManifestResourceStream("Microsoft.IronStudio.Core.Resources.clearscr.gif"); + image.EndInit(); + + var res = new Image(); + res.Source = image; + res.Opacity = 1.0; + res.Width = res.Height = 16; + return res; + + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/EchoReplCommand.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/EchoReplCommand.cs new file mode 100644 index 0000000000..00d1322f20 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/EchoReplCommand.cs @@ -0,0 +1,54 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.IronStudio.Repl; +using System.ComponentModel.Composition; +using System.IO; +using System.Windows.Threading; + +namespace Microsoft.IronStudio.Core.Repl { + [Export(typeof(IReplCommand))] + class EchoReplCommand : IReplCommand { + #region IReplCommand Members + + public void Execute(IReplWindow window, string arguments) { + arguments = arguments .ToLowerInvariant(); + if (arguments == "on") { + window.ShowOutput = true; + } else { + window.ShowOutput = false; + } + } + + public string Description { + get { return "Suppress or unsuppress output to the buffer"; } + } + + public string Command { + get { return "echo"; } + } + + public object ButtonContent { + get { + return null; + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/LoadReplCommand.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/LoadReplCommand.cs new file mode 100644 index 0000000000..40c2126094 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/LoadReplCommand.cs @@ -0,0 +1,56 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.ComponentModel.Composition; +using System.IO; +using Microsoft.IronStudio.Repl; +using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods; + +namespace Microsoft.IronStudio.Core.Repl { + [Export(typeof(IReplCommand))] + class LoadReplCommand : IReplCommand { + #region IReplCommand Members + + public void Execute(IReplWindow window, string arguments) { + using (var stream = new StreamReader(arguments)) { + string line; + while ((line = stream.ReadLine()) != null) { + if (line.StartsWith("%%")) { + window.PasteText(String.Empty); + } else { + window.PasteText(line); + } + } + } + window.PasteText(window.CurrentView.Options.GetNewLineCharacter()); + } + + public string Description { + get { return "Loads commands from file and executes until complete"; } + } + + public string Command { + get { return "load"; } + } + + public object ButtonContent { + get { + return null; + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/ResetReplCommand.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/ResetReplCommand.cs new file mode 100644 index 0000000000..8980c20ade --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/ResetReplCommand.cs @@ -0,0 +1,55 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.ComponentModel.Composition; +using System.Reflection; +using System.Windows.Controls; +using System.Windows.Media.Imaging; +using Microsoft.IronStudio.Repl; + +namespace Microsoft.IronStudio.Core.Repl { + [Export(typeof(IReplCommand))] + class ResetReplCommand : IReplCommand { + #region IReplCommand Members + + public void Execute(IReplWindow window, string arguments) { + window.Reset(); + } + + public string Description { + get { return "Reset to an empty execution engine, but keep REPL history"; } + } + + public string Command { + get { return "reset"; } + } + + public object ButtonContent { + get { + var image = new BitmapImage(); + image.BeginInit(); + image.StreamSource = Assembly.GetExecutingAssembly().GetManifestResourceStream("Microsoft.IronStudio.Core.Resources.ResetSession.gif"); + image.EndInit(); + + var res = new Image(); + res.Source = image; + res.Opacity = 1.0; + res.Width = res.Height = 16; + return res; + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/WaitReplCommand.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/WaitReplCommand.cs new file mode 100644 index 0000000000..a254482d3e --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Commands/WaitReplCommand.cs @@ -0,0 +1,59 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.IronStudio.Repl; +using System.ComponentModel.Composition; +using System.IO; +using System.Windows.Threading; + +namespace Microsoft.IronStudio.Core.Repl { + [Export(typeof(IReplCommand))] + class WaitReplCommand : IReplCommand { + #region IReplCommand Members + + public void Execute(IReplWindow window, string arguments) { + var delay = new TimeSpan(0, 0, 0, 0, int.Parse(arguments)); + var start = DateTime.UtcNow; + while ((start + delay) > DateTime.UtcNow) { + var frame = new DispatcherFrame(); + Dispatcher.CurrentDispatcher.BeginInvoke( + DispatcherPriority.Background, + new Action(f => f.Continue = false), + frame + ); + Dispatcher.PushFrame(frame); + } + } + + public string Description { + get { return "Wait for at least the specified number of milliseconds"; } + } + + public string Command { + get { return "wait"; } + } + + public object ButtonContent { + get { + return null; + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/DlrEvaluator.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/DlrEvaluator.cs new file mode 100644 index 0000000000..318f5247b3 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/DlrEvaluator.cs @@ -0,0 +1,344 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.Remoting; +using System.Threading; +using System.Windows.Threading; +using Microsoft.IronStudio.Core.Repl; +using Microsoft.IronStudio.Repl; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.IronStudio.Library.Repl { + public abstract class DlrEvaluator : ReplEvaluator, IDlrEvaluator { + protected ScriptEngine _engine; + protected ScriptScope _currentScope; + protected Thread _thread; + protected Dispatcher _currentDispatcher; + private int _varCounter; + + // Concrete subclasses constructed via reflection when deserialized from the registry. + protected DlrEvaluator(string/*!*/ language) + : base(language) { + } + + public ScriptEngine Engine { + get { + return _engine; + } + } + + public override void Start() { + Action writer = _output.Write; + _engine = MakeEngine(new OutputStream(writer), new Writer(writer), new Reader(ReadInput)); + } + + public override void Reset() { + InitScope(Engine.CreateScope()); + } + + protected virtual ScriptEngine MakeEngine(System.IO.Stream stream, System.IO.TextWriter writer, System.IO.TextReader reader) { + throw new NotImplementedException(); + } + + protected virtual void InitScope(ScriptScope scope) { + _currentScope = scope ?? _engine.CreateScope(); + } + + protected void InitThread() { + _thread = new Thread(Execute); + _thread.SetApartmentState(ApartmentState.STA); + _thread.Priority = ThreadPriority.Normal; + _thread.Name = String.Format("Execution Engine ({0})", Language); + _thread.IsBackground = true; + _thread.Start(); + } + + protected void OutputResult(object result, ObjectHandle exception) { + if (exception != null) { + WriteException(exception); + } else if (result != null) { + ScopeForLastResult.SetVariable("_", result); + WriteObject(result, _engine.Operations.Format(result)); + } + } + + public override string FormatException(ObjectHandle exception) { + return _engine.GetService().FormatException(exception); + } + + protected virtual ScriptScope ScopeForLastResult { + get { return _currentScope; } + } + + protected CompilerOptions CompilerOptions { + get { return _engine.GetCompilerOptions(_currentScope); } + } + + protected virtual SourceCodeKind SourceCodeKind { + get { return SourceCodeKind.Statements; } + } + + protected Dispatcher Dispatcher { + get { + if (_currentDispatcher == null) { + _currentDispatcher = Dispatcher.FromThread(_thread); + } + return _currentDispatcher; + } + } + + public override bool CanExecuteText(string/*!*/ text) { + var source = _engine.CreateScriptSourceFromString(text, SourceCodeKind); + var result = source.GetCodeProperties(CompilerOptions); + return (result == ScriptCodeParseResult.Empty || + result == ScriptCodeParseResult.Complete || + result == ScriptCodeParseResult.Invalid); + } + + private void FinishExecution(ObjectHandle result, ObjectHandle exception, Action completionFunction) { + _output.Flush(); + if (exception != null) { + OutputResult(null, exception); + } + + if (completionFunction != null) { + completionFunction(exception == null, exception); + } + } + + public override bool ExecuteText(string text, Action completionFunction) { + return ExecuteTextInScopeWorker(text, _currentScope, SourceCodeKind, (r, e) => FinishExecution(r, e, completionFunction)); + } + + private bool ExecuteTextInScopeWorker(string text, ScriptScope scope, SourceCodeKind kind, Action completionFunction) { + var source = _engine.CreateScriptSourceFromString(text, kind); + var errors = new DlrErrorListener(); + var command = source.Compile(CompilerOptions, errors); + if (command == null) { + if (errors.Errors.Count > 0) { + WriteException(new ObjectHandle(errors.Errors[0])); + } + return false; + } + // Allow re-entrant execution. + + Dispatcher.BeginInvoke(new Action(() => { + ObjectHandle result = null; + ObjectHandle exception = null; + try { + result = command.ExecuteAndWrap(scope, out exception); + } catch (ThreadAbortException e) { + if (e.ExceptionState != null) { + exception = new ObjectHandle(e.ExceptionState); + } else { + exception = new ObjectHandle(e); + } + if ((Thread.CurrentThread.ThreadState & System.Threading.ThreadState.AbortRequested) != 0) { + Thread.ResetAbort(); + } + } catch (RemotingException) { + WriteLine("Communication with the remote process has been disconnected."); + } catch (Exception e) { + exception = new ObjectHandle(e); + } + if (completionFunction != null) { + completionFunction(result, exception); + } + })); + + return true; + } + + public virtual ICollection GetMemberNames(string expression) { + var source = _engine.CreateScriptSourceFromString(expression, SourceCodeKind.Expression); + var errors = new DlrErrorListener(); + + if (ShouldEvaluateForCompletion(expression)) { + var command = source.Compile(CompilerOptions, errors); + if (command == null) { + return new MemberDoc[0]; + } + + ObjectHandle exception, obj = command.ExecuteAndWrap(_currentScope, out exception); + if (exception == null) { + var docOps = _engine.GetService(); + if (docOps != null) { + return docOps.GetMembers(obj); + } + + return new List( + _engine.Operations.GetMemberNames(obj).Select( + x => new MemberDoc(x, MemberKind.None) + ) + ); + } + } + + return new MemberDoc[0]; + } + + protected virtual bool ShouldEvaluateForCompletion(string source) { + return true; + } + + public virtual ICollection GetSignatureDocumentation(string expression) { + var source = _engine.CreateScriptSourceFromString(expression, SourceCodeKind.Expression); + if (ShouldEvaluateForCompletion(expression)) { + var errors = new DlrErrorListener(); + var command = source.Compile(CompilerOptions, errors); + if (command == null) { + return new OverloadDoc[0]; + } + + ObjectHandle exception, obj = command.ExecuteAndWrap(_currentScope, out exception); + if (exception == null) { + var docOps = _engine.GetService(); + if (docOps != null) { + return docOps.GetOverloads(obj); + } + + return new[] { + new OverloadDoc( + "", + _engine.Operations.GetDocumentation(obj), + new ParameterDoc[0] + ) + }; + } + } + + return new OverloadDoc[0]; + } + + public override bool CheckSyntax(string text, SourceCodeKind kind) { + var source = _engine.CreateScriptSourceFromString(text, SourceCodeKind); + var errors = new DlrErrorListener(); + var command = source.Compile(CompilerOptions, errors); + if (command == null) { + if (errors.Errors.Count > 0) { + WriteException(new ObjectHandle(errors.Errors[0])); + } + return false; + } + return true; + } + + public override void AbortCommand() { + // TODO: Support for in-proc REPLs + } + + public new void Dispose() { + base.Dispose(); + var dispatcher = Dispatcher.FromThread(_thread); + if (dispatcher != null) { + dispatcher.BeginInvokeShutdown(DispatcherPriority.Send); + } + } + + private void Execute() { + while (!Dispatcher.CurrentDispatcher.HasShutdownStarted) { + try { + Dispatcher.Run(); + } catch (Exception exception) { + try { + OutputResult(null, new ObjectHandle(exception)); + } catch (Exception) { + Restart(); + } + } + } + _engine.Runtime.Shutdown(); + } + + public virtual void Restart() { + } + + public override string InsertData(object data, string prefix) { + if (prefix == null || prefix.Length == 0) { + prefix = "__data"; + } + + while (true) { + var varname = prefix + _varCounter.ToString(); + if (!_currentScope.ContainsVariable(varname)) { + // TODO: Race condition + _currentScope.SetVariable(varname, data); + return varname; + } + _varCounter++; + } + } + + protected virtual string[] FilterNames(IList names, string startsWith) { + // TODO: LINQify once targeting CLR4? + var n = new List(names); + if (startsWith != null && startsWith.Length != 0) { + n.RemoveAll((s) => !s.StartsWith(startsWith)); + } + n.Sort((a, b) => { + int cmp1 = a.ToLowerInvariant().CompareTo(b.ToLowerInvariant()); + if (cmp1 != 0) { + return cmp1; + } + return a.CompareTo(b); + }); + return n.ToArray(); + } + + protected override object GetRootObject() { + return _currentScope; + } + + protected override string[] GetObjectMemberNames(ObjectHandle obj, string startsWith) { + try { + return FilterNames(_engine.Operations.GetMemberNames(obj), startsWith); + } catch { + // TODO: Log error? + return new string[0]; + } + } + + public virtual bool EnableMultipleScopes { + get { + return true; + } + } + + class DlrErrorListener : ErrorListener { + private readonly List _errors = new List(); + + internal List Errors { + get { return _errors; } + } + + public override void ErrorReported(ScriptSource source, string message, SourceSpan span, int errorCode, Severity severity) { + _errors.Add(new SyntaxErrorException( + message, + source.Path, + source.GetCode(), + source.GetCodeLine(span.Start.Line), + span, + errorCode, + severity)); + } + } + } +} + \ No newline at end of file diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/History.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/History.cs new file mode 100644 index 0000000000..92d661f498 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/History.cs @@ -0,0 +1,184 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Collections.ObjectModel; + +namespace Microsoft.IronStudio.Core.Repl { + internal class History { + internal class HistoryEntry { + internal string Text; + internal bool Command; + internal int Duration; + internal bool Failed; + } + + internal class HistoryEventArgs : EventArgs { + private readonly HistoryEntry _historyEntry; + + internal HistoryEventArgs(HistoryEntry entry) { + _historyEntry = entry; + } + + internal HistoryEntry HistoryEntry { + get { return _historyEntry; } + } + } + + private readonly int _maxLength; + private int _pos; + private bool _live; + private readonly ObservableCollection _history; + + internal History() + : this(50) { + } + + internal History(int maxLength) { + _maxLength = maxLength; + _pos = -1; + _live = false; + _history = new ObservableCollection(); + } + + internal int MaxLength { + get { return _maxLength; } + } + + internal int Length { + get { return _history.Count; } + } + + internal IEnumerable Items { + get { return _history; } + } + + internal HistoryEntry Last { + get { + if (_history.Count > 0) { + return _history[_history.Count - 1]; + } else { + return null; + } + } + } + + internal int Position { + get { + if (_pos >= 0) { + return _pos; + } else { + return Length; + } + } + } + + internal void Add(string text) { + var entry = new HistoryEntry { Text = text }; + _live = false; + if (Length == 0 || Last.Text != text) { + _history.Add(entry); + var handler = ItemAdded; + if (handler != null) { + handler(this, new HistoryEventArgs(entry)); + } + } + if (_history[InternalPosition].Text != text) { + _pos = -1; + } + if (Length > MaxLength) { + _history.RemoveAt(0); + if (_pos > 0) { + _pos--; + } + } + } + + private int InternalPosition { + get { + if (_pos == -1) { + return Length - 1; + } else { + return _pos; + } + } + } + + private string GetHistoryText(int pos) { + if (pos < 0) { + pos += Length; + } + return _history[pos].Text; + } + + internal string GetNextText() { + _live = true; + if (Length <= 0 || _pos < 0 || _pos == Length - 1) { + return null; + } + _pos++; + return GetHistoryText(_pos); + } + + internal string GetPreviousText() { + bool wasLive = _live; + _live = true; + if (Length == 0 || (Length > 1 && _pos == 0)) { + return null; + } + if (_pos == -1) { + _pos = Length - 1; + } else if (!wasLive) { + // Handles up up up enter up + // Do nothing + } else { + _pos--; + } + return GetHistoryText(_pos); + } + + private static readonly Func[] _matchFns = new Func[] { + (x, y) => x.ToLowerInvariant().IndexOf(y.ToLowerInvariant()) >= 0, + (x, y) => x.ToLowerInvariant().IndexOf(y.ToLowerInvariant()) == 0, + (x, y) => x.IndexOf(y) >= 0, + (x, y) => x.IndexOf(y) == 0 + }; + + private string FindMatch(string mask, bool caseSensitive, Func moveFn) { + var matchFn = _matchFns[2 * (caseSensitive ? 1 : 0) + 1]; + var startPos = _pos; + while (true) { + string next = moveFn(); + if (next == null) { + _pos = startPos; + return null; + } + if (matchFn(next, mask)) { + return next; + } + } + } + + internal string FindNext(string mask) { + return FindMatch(mask, false, GetNextText); + } + + internal string FindPrevious(string mask) { + return FindMatch(mask, false, GetPreviousText); + } + + internal event EventHandler ItemAdded; + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/IMultipleScopeEvaluator.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/IMultipleScopeEvaluator.cs new file mode 100644 index 0000000000..aa8c202fcd --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/IMultipleScopeEvaluator.cs @@ -0,0 +1,37 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.IronStudio.Repl { + /// + /// Supports a REPL evaluator which enables the user to switch between + /// multiple scopes of execution. + /// + public interface IMultipleScopeEvaluator : IReplEvaluator { + IEnumerable GetAvailableScopes(); + event EventHandler AvailableScopesChanged; + event EventHandler CurrentScopeChanged; + void SetScope(string scopeName); + string CurrentScopeName { + get; + } + bool EnableMultipleScopes { + get; + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/IReplCommand.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/IReplCommand.cs new file mode 100644 index 0000000000..901cf39284 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/IReplCommand.cs @@ -0,0 +1,54 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.IronStudio.Repl { + /// + /// Represents a command which can be run from a REPL window. + /// + /// This interface is a MEF contract and can be implemented and exported to add commands to the REPL window. + /// + public interface IReplCommand { + /// + /// Runs the specified command with the given arguments. The command may interact + /// with the window using the provided interface. + /// + void Execute(IReplWindow window, string arguments); + + /// + /// Gets a description of the REPL command which is displayed when the user asks for help. + /// + string Description { + get; + } + + /// + /// Gets the text for the actual command. + /// + string Command { + get; + } + + /// + /// Content to be placed in a toolbar button or null if should not be placed on a toolbar. + /// + object ButtonContent { + get; + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/IReplEvaluator.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/IReplEvaluator.cs new file mode 100644 index 0000000000..ab9ca17b0a --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/IReplEvaluator.cs @@ -0,0 +1,64 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using System.Runtime.Remoting; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.IronStudio.Repl { + public struct Output { + public string Text { get; set; } + public object Object { get; set; } + } + + public interface IReplEvaluator : IDisposable { + void Start(); + void TextViewCreated(IReplWindow window, ITextView view); + + string/*!*/ Prompt { get; } + string/*!*/ CommandPrefix { get; } + bool DisplayPromptInMargin { get; } + + // Parsing and Execution + bool CheckSyntax(string text, SourceCodeKind kind); + bool CanExecuteText(string/*!*/ text); + bool ExecuteText(string text, Action completion); + void AbortCommand(); + string InsertData(object data, string prefix); + string FormatException(ObjectHandle exception); + void Reset(); + + // IO + void FlushOutput(); + void SetIO(Action outputDelegate, Func inputDelegate); + + // Intellisense support + /* + string Language { get; } + Member[] GetCompletions(string text); + Member[] GetModules(); + ReplOverloadResult[] GetSignatures(string text); + + // Workspace support + WorkspaceVariable[] GetVariablesInGlobalScope();*/ + } + + public interface IDlrEvaluator : IReplEvaluator { + ICollection GetMemberNames(string expression); + ICollection GetSignatureDocumentation(string expression); + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/IReplWindow.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/IReplWindow.cs new file mode 100644 index 0000000000..b9d179974c --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/IReplWindow.cs @@ -0,0 +1,113 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using System.Windows; + +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronStudio.Repl { + /// + /// An implementation of a Read Eval Print Loop Window for iteratively developing code. + /// + public interface IReplWindow { + /// + /// Gets the ITextCaret in which the REPL window is executing. + /// + ITextCaret Caret { + get; + } + + /// + /// WPF Content of the Repl Window + /// + FrameworkElement Content { + get; + } + + /// + /// Content type in the Repl Window + /// + IContentType ContentType { + get; + } + + /// + /// Gets the IWpfTextView in which the REPL window is executing. + /// + IWpfTextView CurrentView { + get; + } + + /// + /// The language evaluator used in Repl Window + /// + IReplEvaluator Evaluator { + get; + } + + /// + /// Gets or sets whether output from scripts should be echoed to the window. + /// + bool ShowOutput { + get; + set; + } + + /// + /// Gets or sets whether the up/down arrows support navigating history when at the end of the current input. + /// + bool UseSmartUpDown { + get; + set; + } + + /// + /// Title of the Repl Window + /// + string Title { + get; + } + + /// + /// Clears the REPL window screen. + /// + void ClearScreen(); + + void Focus(); + + /// + /// Pastes the specified text in as if the user had typed it. + /// + /// + void PasteText(string text); + + /// + /// Resets the execution context clearing all variables. + /// + void Reset(); + + void AbortCommand(); + + /// + /// Writes a line into the output buffer as if it was outputted by the program. + /// + /// + void WriteLine(string text); + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/IReplWindowCommands.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/IReplWindowCommands.cs new file mode 100644 index 0000000000..17db4e01f7 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/IReplWindowCommands.cs @@ -0,0 +1,76 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ +using System; + +namespace Microsoft.IronStudio.Repl { + public interface IReplWindowCommands { + + /// + /// Shift+VK_RETURN + /// + void BreakLine(); + + /// + /// VSConstants.VSStd2KCmdID.CANCEL + /// + void Cancel(); + + /// + /// VSConstants.VSStd2KCmdID.UP + /// + void SmartUpArrow(); + + /// + /// VSConstants.VSStd2KCmdID.DOWN + /// + void SmartDownArrow(); + + /// + /// + /// VSConstants.VSStd2KCmdID.BOL (false) + /// VSConstants.VSStd2KCmdID.BOL_EXT (true) + /// + /// + /// When on the first line of the current input region but not at the + /// first position, move to the beginning of the region. Otherwise, move + /// to the beginning of the line. + void Home(bool extendSelection); + + /// + /// VSConstants.VSStd97CmdID.Paste + /// + /// + bool PasteClipboard(); + + /// + /// VSConstants.VSStd97CmdID.SelectAll + /// + /// + /// When in any input region, select that region. If not in an input + /// region or if selection already corresponds to an input region, select + /// the entire document + /// + bool SelectAll(); + + /// + /// If currently in the active edit executes the command. Otherwise if we're + /// in an old input region pastes the current text into the active edit. + /// + void ExecuteOrPasteSelected(); + + void HistoryNext(); + + void HistoryPrevious(); + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/OutputBuffer.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/OutputBuffer.cs new file mode 100644 index 0000000000..db2dfbc76e --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/OutputBuffer.cs @@ -0,0 +1,94 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Diagnostics; +using System.Text; +using System.Timers; + +namespace Microsoft.IronStudio.Library.Repl { + public class OutputBuffer : IDisposable { + private readonly Timer _timer; + private int _maxSize; + private readonly object _lock; + private readonly StringBuilder _buffer; + private long _lastFlush; + private static readonly Stopwatch _stopwatch; + public event Action OutputText; + private const int _initialMaxSize = 1024; + + public OutputBuffer() + : this(400) { + } + + static OutputBuffer() { + _stopwatch = new Stopwatch(); + _stopwatch.Start(); + } + + public OutputBuffer(int timeout) { + _maxSize = _initialMaxSize; + _lock = new object(); + _buffer = new StringBuilder(_maxSize); + _timer = new Timer(); + _timer.Elapsed += (sender, args) => Flush(); + _timer.Interval = timeout; + } + + public void Write(string text) { + bool needsFlush = false; + lock (_lock) { + _buffer.Append(text); + needsFlush = (_buffer.Length > _maxSize); + if (!needsFlush && !_timer.Enabled) { + _timer.Enabled = true; + } + } + if (needsFlush) { + Flush(); + } + } + + public void Flush() { + // if we're rapidly outputting grow the output buffer. + long curTime = _stopwatch.ElapsedMilliseconds; + if ((curTime - _lastFlush) < 1000) { + if (_maxSize < (1024 * 1024)) { + _maxSize *= 2; + } + } + _lastFlush = _stopwatch.ElapsedMilliseconds; + + string result; + lock (_lock) { + result = _buffer.ToString(); + _buffer.Length = 0; + if (_buffer.Capacity > _maxSize) { + _buffer.Capacity = _maxSize; + } + _timer.Enabled = false; + } + if (result.Length > 0) { + Action evt = OutputText; + if (evt != null) { + evt(result); + } + } + } + + public void Dispose() { + _timer.Enabled = false; + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/OutputStream.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/OutputStream.cs new file mode 100644 index 0000000000..e4c5ed9edc --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/OutputStream.cs @@ -0,0 +1,76 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Text; + +namespace Microsoft.IronStudio.Core.Repl { + internal class OutputStream : Stream { + private static readonly Encoding _utf8 = new UTF8Encoding(false, false); + private readonly Action _writer; + + public OutputStream(Action writer) { + _writer = writer; + } + + public override void Write(byte[] buffer, int offset, int count) { + _writer(_utf8.GetString(buffer, offset, count)); + } + + public override object InitializeLifetimeService() { + return null; + } + + public override bool CanRead { + get { return false; } + } + + public override bool CanSeek { + get { return false; } + } + + public override bool CanWrite { + get { return false; } + } + + public override void Flush() { + } + + public override long Length { + get { throw new NotImplementedException(); } + } + + public override long Position { + get { + throw new NotImplementedException(); + } + set { + throw new NotImplementedException(); + } + } + + public override int Read(byte[] buffer, int offset, int count) { + throw new NotImplementedException(); + } + + public override long Seek(long offset, SeekOrigin origin) { + throw new NotImplementedException(); + } + + public override void SetLength(long value) { + throw new NotImplementedException(); + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Reader.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Reader.cs new file mode 100644 index 0000000000..7d4f75e9de --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Reader.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.IO; + +namespace Microsoft.IronStudio.Core.Repl { + class Reader : TextReader { + private readonly Func _readInput; + private string _read; + private int _curIndex; + + public Reader(Func ReadInput) { + _readInput = ReadInput; + } + + public override int Read(char[] buffer, int index, int count) { + ReadBuffer(); + + int copying = Math.Min(count, _read.Length - _curIndex); + _read.CopyTo(_curIndex, buffer, index, copying); + _curIndex += copying; + return copying; + } + + public override int Read() { + ReadBuffer(); + + if (_read.Length == 0) { + return -1; + } + + return _read[_curIndex++]; + } + + private void ReadBuffer() { + if (_read == null || _curIndex >= _read.Length) { + _read = _readInput(); + _curIndex = 0; + } + } + + public override object InitializeLifetimeService() { + return null; + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ReplEvaluator.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ReplEvaluator.cs new file mode 100644 index 0000000000..47a3d15d69 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ReplEvaluator.cs @@ -0,0 +1,166 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Runtime.Remoting; +using Microsoft.IronStudio.Repl; +using Microsoft.Scripting; +using Microsoft.Scripting.Utils; +using Microsoft.VisualStudio.Text.Editor; + +namespace Microsoft.IronStudio.Library.Repl { + public abstract class ReplEvaluator : MarshalByRefObject, IDisposable, IReplEvaluator { + private readonly string/*!*/ _language; + protected readonly OutputBuffer/*!*/ _output; + + public Action WriteOutput; + public Func ReadInput; + + // Concrete subclasses constructed via reflection when deserialized from the registry. + protected ReplEvaluator(string/*!*/ language) { + ContractUtils.RequiresNotNull(language, "language"); + + _language = language; + _output = new OutputBuffer(); + _output.OutputText += OnFlushText; + } + + public virtual void TextViewCreated(IReplWindow window, ITextView view) { + } + + public virtual void Start() { + } + + public virtual void Reset() { + } + + public virtual string FormatException(ObjectHandle exception) { + return exception.ToString(); + } + + public void SetIO(Action outputDelegate, Func inputDelegate) { + WriteOutput = outputDelegate; + ReadInput = inputDelegate; + } + + private void OnFlushText(string text) { + var writeOutput = WriteOutput; + if (writeOutput != null) { + writeOutput(null, new Output { Text = text }); + } + } + + public void FlushOutput() { + _output.Flush(); + } + + protected internal void WriteObject(object obj, string text) { + FlushOutput(); + + var writeOutput = WriteOutput; + if (writeOutput != null) { + writeOutput(null, new Output { Text = text, Object = obj }); + } + } + + protected void Write(string text) { + _output.Write(text); + } + + protected void WriteLine(string text) { + _output.Write(text); + _output.Write("\r\n"); + } + + protected void WriteException(ObjectHandle exception) { + WriteObject(exception, FormatException(exception)); + } + + public string Language { + get { return _language; } + } + + public virtual bool CanExecuteText(string text) { + return true; + } + + abstract public bool ExecuteText(string text, Action completion); + + public virtual bool CheckSyntax(string text, SourceCodeKind kind) { + return true; + } + + public virtual void AbortCommand() { + } + + public virtual void Dispose() { + AbortCommand(); + _output.Dispose(); + } + + public virtual string InsertData(object data, string prefix) { + return null; + } +#if FALSE + public virtual Member[] GetCompletions(string text) { + string[] symbols = text.Split('.'); + object obj = GetRootObject(); + if (obj == null) { + return Member.None; + } + for (int i = 0; i < symbols.Length - 1; i++) { + obj = GetObjectMember(obj, symbols[i]); + if (obj == null) { + return Member.None; + } + } + + string lastSymbol = symbols[symbols.Length - 1]; + var members = GetObjectMemberNames(obj, lastSymbol); + var result = new Member[members.Length]; + for (int i = 0; i < members.Length; i++) { + result[i] = new Member { Name = members[i] }; + } + return result; + } + + public virtual Member[] GetModules() { + return Member.None; + } + + public virtual ReplOverloadResult[] GetSignatures(string text) { + return new ReplOverloadResult[0]; + } +#endif + protected virtual object GetRootObject() { + return null; + } + + protected virtual string[] GetObjectMemberNames(ObjectHandle obj, string startsWith) { + return ArrayUtils.EmptyStrings; + } + + public virtual string/*!*/ Prompt { + get { return "»"; } + } + + public virtual string/*!*/ CommandPrefix { + get { return "%"; } + } + + public virtual bool DisplayPromptInMargin { + get { return false; } + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ReplIntellisenseMode.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ReplIntellisenseMode.cs new file mode 100644 index 0000000000..1aba5c673d --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ReplIntellisenseMode.cs @@ -0,0 +1,26 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; + +namespace Microsoft.IronStudio.Core.Repl { + public enum ReplIntellisenseMode { + NeverEvaluate, + DontEvaluateCalls, + AlwaysEvaluate, + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ReplPrompt.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ReplPrompt.cs new file mode 100644 index 0000000000..c88532ee08 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ReplPrompt.cs @@ -0,0 +1,139 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Media; +using Microsoft.Scripting.Utils; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Formatting; +using Microsoft.VisualStudio.Text.Tagging; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronStudio.Core.Repl { + /// + /// Any with content type , role + /// and property gets prompt glyphs in its glyph margin. + /// + public interface IReplPromptProvider { + /// + /// The prompt text to display in the margin. + /// + string/*!*/ Prompt { get; } + + /// + /// The control that hosts the text view. + /// + Control/*!*/ HostControl { get; } + + /// + /// Should we draw a prompt glyph for given line. + /// + bool HasPromptForLine(ITextSnapshot/*!*/ snapshot, int lineNumber); + + /// + /// Notifies glyph margin that prompt glyph(s) need to be updated. + /// + event Action PromptChanged; + } + + /// + /// Implements prompt glyphs in a GlyphMargin. + /// + internal static class ReplPrompt { + internal const string GlyphName = "ReplPromptGlyph"; + + internal sealed class ReplGlyphTag : IGlyphTag { + internal static readonly ReplGlyphTag Instance = new ReplGlyphTag(); + } + + internal sealed class Tagger : ITagger { + private readonly IReplPromptProvider/*!*/ _promptProvider; + + public Tagger(IReplPromptProvider/*!*/ promptProvider) { + Assert.NotNull(promptProvider); + _promptProvider = promptProvider; + _promptProvider.PromptChanged += new Action((span) => { + var tagsChanged = TagsChanged; + if (tagsChanged != null) { + tagsChanged(this, new SnapshotSpanEventArgs(span)); + } + }); + } + + public IEnumerable>/*!*/ GetTags(NormalizedSnapshotSpanCollection/*!*/ spans) { + foreach (SnapshotSpan span in spans) { + if (_promptProvider.HasPromptForLine(span.Snapshot, span.Start.GetContainingLine().LineNumber)) { + yield return new TagSpan(span, ReplGlyphTag.Instance); + } + } + } + + public event EventHandler TagsChanged; + } + + [Export(typeof(ITaggerProvider))] + [TagType(typeof(ReplGlyphTag))] + [ContentType(CoreConstants.DlrContentTypeName)] + [TextViewRole(CoreConstants.ReplTextViewRole)] + internal sealed class TaggerProvider : ITaggerProvider { + public ITagger CreateTagger(ITextBuffer/*!*/ buffer) where T : ITag { + IReplPromptProvider promptProvider; + if (buffer.Properties.TryGetProperty(typeof(IReplPromptProvider), out promptProvider)) { + return (ITagger)(object)new Tagger(promptProvider); + } + return null; + } + } + + internal sealed class GlyphFactory : IGlyphFactory { + private readonly IReplPromptProvider/*!*/ _promptProvider; + private static readonly FontFamily _Consolas = new FontFamily("Consolas"); + + public GlyphFactory(IReplPromptProvider/*!*/ promptProvider) { + Assert.NotNull(promptProvider); + _promptProvider = promptProvider; + } + + public UIElement/*!*/ GenerateGlyph(IWpfTextViewLine/*!*/ line, IGlyphTag tag) { + TextBlock block = new TextBlock(); + block.Text = _promptProvider.Prompt; + block.Foreground = _promptProvider.HostControl.Foreground; + block.FontSize = _promptProvider.HostControl.FontSize; + block.FontFamily = _Consolas; // TODO: get the font family from the editor? + return block; + } + } + + [Export(typeof(IGlyphFactoryProvider))] + [Name(GlyphName)] + [Order(After = "VsTextMarker")] + [TagType(typeof(ReplGlyphTag))] + [ContentType(CoreConstants.DlrContentTypeName)] + [TextViewRole(CoreConstants.ReplTextViewRole)] + internal sealed class GlyphFactoryProvider : IGlyphFactoryProvider { + public IGlyphFactory GetGlyphFactory(IWpfTextView/*!*/ view, IWpfTextViewMargin/*!*/ margin) { + IReplPromptProvider promptProvider; + if (view.TextBuffer.Properties.TryGetProperty(typeof(IReplPromptProvider), out promptProvider)) { + return new GlyphFactory(promptProvider); + } + return null; + } + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ReplSpan.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ReplSpan.cs new file mode 100644 index 0000000000..582f430b84 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ReplSpan.cs @@ -0,0 +1,31 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronStudio.Library.Repl { + public class ReplSpan { + public ReplSpan(bool wasCommand, bool wasException, SnapshotSpan input, SnapshotSpan? output) { + WasCommand = wasCommand; + WasException = wasException; + Input = input; + Output = output; + } + public readonly bool WasCommand; + public readonly bool WasException; + public readonly SnapshotSpan Input; + public readonly SnapshotSpan? Output; + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ReplWindow.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ReplWindow.cs new file mode 100644 index 0000000000..02fb797a14 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ReplWindow.cs @@ -0,0 +1,1582 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Diagnostics; +using System.Linq; +using System.Runtime.Remoting; +using System.Threading; +using System.Windows; +using System.Windows.Controls; +using System.Windows.Input; +using System.Windows.Media; +using System.Windows.Threading; +using Microsoft.IronStudio.Core.Repl; +using Microsoft.IronStudio.Repl; +using Microsoft.Scripting.Utils; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Language.Intellisense; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text.Editor.OptionsExtensionMethods; +using Microsoft.VisualStudio.Text.Operations; +using Microsoft.VisualStudio.Utilities; + +namespace Microsoft.IronStudio.Library.Repl { + + public class ReplWindow : IReplWindow, IReplWindowCommands, IReplPromptProvider, IMixedBuffer, IDisposable { + private DockPanel _panel; + private ComboBox _scopebox; + private AutoResetEvent _inputEvent = new AutoResetEvent(false); + private bool _useSmartUpDown; + + private bool _isRunning; + private bool _showOutput; + + private IClassifier _classifier; + private IIntellisenseSessionStack _sessionStack; + private ITrackingPoint _inputPoint; + private ITrackingPoint _outputPoint; + private Action _processResult; + private Stopwatch _sw; + private string _unCommittedInput; + private DispatcherTimer _executionTimer; + private Cursor _oldCursor; + private List _commands; + private IWpfTextViewHost _textViewHost; + private IEditorOperations _editorOperations; + + private readonly IComponentModel/*!*/ _componentModel; + private readonly IReplEvaluator/*!*/ _evaluator; + private readonly IContentType/*!*/ _contentType; + private readonly string/*!*/ _title; + private readonly List/*!*/ _inputSpans; + private readonly List/*!*/ _errorInputs; + private readonly List/*!*/ _pendingInput; + private readonly List/*!*/ _demoCommands; + private readonly History/*!*/ _history; // TODO: history provider should be loaded via MEF? + + // We use one or two regions to protect span [0, input start) from modifications: + private readonly IReadOnlyRegion[]/*!*/ _readOnlyRegions; + + private readonly string/*!*/ _commandPrefix; + private readonly string/*!*/ _prompt; + + private struct PendingInput { + public readonly string Text; + public readonly bool HasNewline; + + public PendingInput(string text, bool hasNewline) { + Text = text; + HasNewline = hasNewline; + } + } + + #region Construction + + public ReplWindow(IComponentModel/*!*/ model, IReplEvaluator/*!*/ evaluator, IContentType/*!*/ contentType, string/*!*/ title) { + ContractUtils.RequiresNotNull(evaluator, "evaluator"); + ContractUtils.RequiresNotNull(contentType, "contentType"); + ContractUtils.RequiresNotNull(title, "title"); + ContractUtils.RequiresNotNull(model, "model"); + + _componentModel = model; + _evaluator = evaluator; + _contentType = contentType; + _title = title; + + _commandPrefix = _evaluator.CommandPrefix; + _prompt = _evaluator.Prompt; + + ContractUtils.Requires(_commandPrefix != null && _prompt != null); + + _readOnlyRegions = new IReadOnlyRegion[2]; + _pendingInput = new List(); + _inputSpans = new List(); + + _errorInputs = new List(); + + _history = new History(); + _demoCommands = new List(); + + SetupOutput(); + } + + public void Initialize() { + _textViewHost = CreateTextViewHost(); + + var textView = _textViewHost.TextView; + + textView.Options.SetOptionValue(DefaultTextViewHostOptions.HorizontalScrollBarId, false); + textView.Options.SetOptionValue(DefaultTextViewHostOptions.LineNumberMarginId, false); + textView.Options.SetOptionValue(DefaultTextViewHostOptions.OutliningMarginId, false); + textView.Options.SetOptionValue(DefaultTextViewHostOptions.GlyphMarginId, _evaluator.DisplayPromptInMargin); + textView.Options.SetOptionValue(DefaultTextViewOptions.WordWrapStyleId, WordWrapStyles.WordWrap); + + _editorOperations = GetEditorOperations(textView); + + StartEvaluator(); + + _commands = CreateCommands(); + CreateUI(); + + Evaluator.TextViewCreated(this, textView); + + textView.TextBuffer.Properties.AddProperty(typeof(IReplEvaluator), _evaluator); + // + // WARNING: We must set following properties only after everything is inititalized. + // If we set them earlier we would expose uninitialized repl window properties. + // + textView.TextBuffer.Properties.AddProperty(typeof(IMixedBuffer), this); + textView.TextBuffer.Properties.AddProperty(typeof(IReplWindowCommands), this); + + // the margin publishes itself in the properties upon creation: + // textView.TextBuffer.Properties.TryGetProperty(typeof(ReplMargin), out _margin); + + //if (_evaluator.DisplayPromptInMargin) { + // _margin = _textViewHost.GetTextViewMargin(PredefinedMarginNames.Glyph); + //} + + PrepareForInput(); + ApplyProtection(); + } + + private List/*!*/ CreateCommands() { + var commands = new List(); + var commandTypes = new HashSet(); + foreach (var command in _componentModel.GetExtensions()) { + // avoid duplicate commands + if (commandTypes.Contains(command.GetType())) { + continue; + } else { + commandTypes.Add(command.GetType()); + } + + commands.Add(command); + } + return commands; + } + + private void CreateUI() { + // background: new SolidColorBrush(Color.FromArgb(0, 188, 199, 216)); + _panel = new DockPanel(); + + ToolBarTray tray = new ToolBarTray(); + ToolBar toolBar = new ToolBar(); + tray.ToolBars.Add(toolBar); + + tray.Background = new SolidColorBrush(Color.FromArgb(255, 173, 185, 205)); + toolBar.Background = new SolidColorBrush(Color.FromArgb(255, 188, 199, 216)); + + IMultipleScopeEvaluator multiScopeEval = Evaluator as IMultipleScopeEvaluator; + if (multiScopeEval != null && multiScopeEval.EnableMultipleScopes) { + AddScopeBox(toolBar, multiScopeEval); + } + + foreach (var command in _commands) { + AddToolBarButton(toolBar, command); + } + + //_toolbar.Children.Add(comboBox); + DockPanel.SetDock(tray, Dock.Top); + + _panel.Children.Add(tray); + + _panel.Children.Add((UIElement)_textViewHost); + } + + #endregion + + #region To Be Moved Into Contracts? + + public event Action ExecutionFinished; + + public IEditorOperations EditorOperations { + get { + return _editorOperations; + } + } + + public IComponentModel ComponentModel { + get { + return _componentModel; + } + } + + public ITextBuffer/*!*/ TextBuffer { + get { return CurrentView.TextBuffer; } + } + + public IClassifier Classifier { + get { + if (_classifier == null) { + var aggregator = ComponentModel.GetService(); + _classifier = aggregator.GetClassifier(TextBuffer); + } + + return _classifier; + } + } + + public ITextSnapshot CurrentSnapshot { + get { return TextBuffer.CurrentSnapshot; } + } + + public string CurrentLine { + get { + return Caret.Position.BufferPosition.GetContainingLine().GetText(); + } + } + + protected virtual IWpfTextViewHost CreateTextViewHost() { + var textBufferFactoryService = ComponentModel.GetService(); + var textBuffer = textBufferFactoryService.CreateTextBuffer(ContentType); + + // we need to set IReplProptProvider property before TextViewHost is instantiated so that ReplPromptTaggerProvider can bind to it + if (Evaluator.DisplayPromptInMargin) { + textBuffer.Properties.AddProperty(typeof(IReplPromptProvider), this); + } + + ITextEditorFactoryService textEditorFactoryService = ComponentModel.GetService(); + ITextViewRoleSet roles = GetReplRoles(); + + var textView = textEditorFactoryService.CreateTextView(textBuffer, roles); + return textEditorFactoryService.CreateTextViewHost(textView, false); + } + + protected ITextViewRoleSet/*!*/ GetReplRoles() { + var textEditorFactoryService = ComponentModel.GetService(); + return textEditorFactoryService.CreateTextViewRoleSet( + PredefinedTextViewRoles.Analyzable, + PredefinedTextViewRoles.Editable, + PredefinedTextViewRoles.Interactive, + PredefinedTextViewRoles.Zoomable, + PredefinedTextViewRoles.Document, + CoreConstants.ReplTextViewRole + ); + } + + #endregion + + #region IReplWindow + + /// + /// See IReplWindow + /// + public ITextCaret Caret { + get { + return CurrentView.Caret; + } + } + + /// + /// See IReplWindow + /// + public FrameworkElement/*!*/ Content { + get { + return _panel; + } + } + + + /// + /// Content type provided by the evaluator. + /// + public IContentType/*!*/ ContentType { + get { + return _contentType; + } + } + + /// + /// See IReplWindow + /// + public IWpfTextView/*!*/ CurrentView { + get { + return _textViewHost.TextView; + } + } + + /// + /// See IReplWindow + /// + public IReplEvaluator/*!*/ Evaluator { + get { + return _evaluator; + } + } + + /// + /// See IReplWindow + /// + public bool ShowOutput { + get { + return _showOutput; + } + set { + Evaluator.FlushOutput(); + _showOutput = value; + } + } + + /// + /// See IReplWindow + /// + public string/*!*/ Title { + get { + return _title; + } + } + + /// + /// See IReplWindow + /// + public void ClearScreen() { + if (!CheckAccess()) { + Dispatcher.Invoke(new Action(() => ClearScreen())); + return; + } + + RemoveProtection(); + + _inputSpans.Clear(); + _errorInputs.Clear(); + _inputPoint = null; + + using (var edit = TextBuffer.CreateEdit()) { + edit.Delete(0, CurrentSnapshot.Length); + edit.Apply(); + } + + PrepareForInput(); + ApplyProtection(); + } + + /// + /// See IReplWindow + /// + public void Focus() { + var textView = CurrentView; + + IInputElement input = textView as IInputElement; + if (input != null) { + Keyboard.Focus(input); + } + } + + /// + /// See IReplWindow + /// + public void PasteText(string text) { + if (!CheckAccess()) { + Dispatcher.BeginInvoke(new Action(() => PasteText(text))); + return; + } + + if (_isRunning) { + return; + } + + if (text.IndexOf('\n') >= 0) { + if (IsCaretProtected) { + _editorOperations.MoveToEndOfDocument(false); + } + + // TODO: Use a language service to parse out individual commands rather + // than pretending each line has been typed in manually + _pendingInput.AddRange(SplitLines(text)); + ProcessPendingInput(); + } else { + if (_editorOperations.SelectedText != "") { + _editorOperations.Delete(); + } + + _editorOperations.InsertText(text); + } + } + + /// + /// See IReplWindow + /// + public void Reset() { + WriteLine("Resetting execution engine"); + Evaluator.Reset(); + } + + public void AbortCommand() { + Evaluator.AbortCommand(); + EnsureNewLine(); + PrepareForInput(); + } + + /// + /// See IReplWindow + /// + public void WriteLine(string text) { + Write(text + _textViewHost.TextView.Options.GetNewLineCharacter()); + } + + #endregion + + #region IReplWindowCommands + + /// + /// See IReplWindowCommands + /// + public void BreakLine() { + AutoIndent.HandleReturn(this); + } + + /// + /// See IReplWindowCommands + /// + public void Cancel() { + ClearCurrentInput(); + } + + /// + /// See IReplWindowCommands + /// + public void SmartUpArrow() { + UIThread(() => { + if (!((IIntellisenseCommandTarget)this.SessionStack).ExecuteKeyboardCommand(IntellisenseKeyboardCommand.Up)) { + // uparrow and downarrow at the end of input or with empty input rotate history + // with multi-line input, uparrow and downarrow move around in text + if (!_isRunning && CaretAtEnd && UseSmartUpDown) { + HistoryPrevious(); + } else { + _editorOperations.MoveLineUp(false); + } + } + }); + } + + public bool UseSmartUpDown { + get { + return _useSmartUpDown; + } + set { + _useSmartUpDown = value; + } + } + + public void HistoryPrevious() { + var found = _history.FindPrevious(""); + if (found != null) { + StoreUncommittedInput(); + SelectHistoryItem(found); + } + } + + private bool IsSingleLineInput { + get { + return ActiveInput.Split('\n').Length == 1; + } + } + + /// + /// See IReplWindowCommands + /// + public void SmartDownArrow() { + UIThread(() => { + if (!((IIntellisenseCommandTarget)this.SessionStack).ExecuteKeyboardCommand(IntellisenseKeyboardCommand.Down)) { + if (!_isRunning && CaretAtEnd && UseSmartUpDown) { + HistoryNext(); + } else { + _editorOperations.MoveLineDown(false); + } + } + }); + } + + public void HistoryNext() { + var found = _history.FindNext(""); + if (found != null) { + StoreUncommittedInput(); + SelectHistoryItem(found); + } else { + InsertUncommittedInput(); + } + } + + /// + /// See IReplWindowCommands + /// + public void Home(bool extendSelection) { + UIThread(() => { + var caret = Caret; + var currentInput = MakeInputSpan(); + + if (currentInput != null) { + var start = currentInput.GetSpan(CurrentSnapshot).Start; + int lineNumber = CurrentSnapshot.GetLineNumberFromPosition(start.Position); + + if (CurrentSnapshot.GetLineNumberFromPosition(caret.Position.BufferPosition) != lineNumber) { + _editorOperations.MoveToStartOfLine(extendSelection); + } else if (extendSelection) { + VirtualSnapshotPoint anchor = CurrentView.Selection.AnchorPoint; + caret.MoveTo(start); + CurrentView.Selection.Select(anchor.TranslateTo(CurrentView.TextSnapshot), CurrentView.Caret.Position.VirtualBufferPosition); + } else { + CurrentView.Selection.Clear(); + caret.MoveTo(start); + } + } else { + _editorOperations.MoveToStartOfLine(extendSelection); + } + }); + } + + /// + /// See IReplWindowCommands + /// + public bool PasteClipboard() { + return UIThread(() => { + // TODO: + //var handler = TryGetSho(); + //if (handler != null) { + // var data = handler(); + // if (data) { + // var varname = Evaluator.InsertData(data, "__data"); + // PasteText(varname); + // ExecuteText(); + // return true; + // } + //} + if (Clipboard.ContainsText()) { + PasteText(Clipboard.GetText()); + } else if (Clipboard.ContainsImage()) { + var image = Clipboard.GetImage(); + var varname = Evaluator.InsertData(image, "__data"); + PasteText(varname); + } else { + return false; + } + return true; + }); + } + + /// + /// See IReplWindowCommands + /// + public bool SelectAll() { + return UIThread(() => { + var ts = CurrentView.Selection; + var region = GetInputSpanContainingCaret(); + if (region != null && (ts.SelectedSpans.Count == 0 || region != ts.SelectedSpans[0])) { + ts.Select(region.Value, true); + return true; + } + return false; + }); + } + + #endregion + + #region IMixedBuffer + + /// + /// See IMixedBuffer + /// + public SnapshotSpan[]/*!*/ GetLanguageSpans(ITextSnapshot/*!*/ snapshot) { + var result = new List(); + foreach (var span in GetAllSpans(snapshot)) { + if (!span.WasCommand) { + // All the input spans have their CRLFs stripped off. Add to the span to compensate. + if (span.Input.End.Position + 2 < span.Input.Snapshot.Length) { + result.Add(new SnapshotSpan(span.Input.Start, span.Input.End + 2)); + } else { + result.Add(span.Input); + } + } + } + +#if DEBUG + // we should never include redundant spans + for (int i = 1; i < result.Count; i++) { + Debug.Assert(result[i].Start.Position > result[i - 1].Start.Position); + } +#endif + return result.ToArray(); + } + + /// + /// See IMixedBuffer + /// + public SnapshotSpan? GetLanguageSpanForLine(ITextSnapshot snapshot, int line) { + return UIThread(() => { + // check if the current input is a relevant span + var currentInput = MakeInputSpan(false, snapshot); + if (currentInput != null) { + var res = GetSpanFromInput(snapshot, line, currentInput); + if (res != null) { + return res; + } + } + + // then check the previous inputs + foreach (var curInput in _inputSpans) { + var res = GetSpanFromInput(snapshot, line, curInput); + if (res != null) { + return res; + } + } + return null; + }); + } + + #endregion + + #region IReplProptProvider Members + + bool IReplPromptProvider.HasPromptForLine(ITextSnapshot/*!*/ snapshot, int lineNumber) { + return UIThread(() => { + if (!_isRunning && + (_inputPoint != null && snapshot.GetLineNumberFromPosition(_inputPoint.GetPosition(snapshot)) == lineNumber || + _inputPoint == null && lineNumber == snapshot.LineCount - 1)) { + return true; + } + + // TODO: bin search? + foreach (var input in _inputSpans) { + if (snapshot.GetLineNumberFromPosition(input.GetStartPoint(snapshot)) == lineNumber) { + return true; + } + } + return false; + }); + } + + public event Action PromptChanged; + + string/*!*/ IReplPromptProvider.Prompt { + get { return _prompt; } + } + + Control/*!*/ IReplPromptProvider.HostControl { + get { return _textViewHost.HostControl; } + } + + #endregion + + #region IDisposable + + /// + /// See IDisposable + /// + public void Dispose() { + Dispose(true); + } + + #endregion + + #region Protected Methods + + protected virtual void Dispose(bool disposing) { + if (disposing) { + Evaluator.Dispose(); + + _commands = null; + } + } + + #endregion + + #region Internal Methods + + internal bool CanExecuteInput() { + var input = ActiveInput; + if (input.Trim().Length == 0) { + // Always allow "execution" of a blank line. + // This will just close the current prompt and start a new one + return true; + } + + // Ignore any whitespace past the insertion point when determining + // whether or not we're at the end of the input + var pt = RelativeInsertionPoint; + var atEnd = (pt == input.Length) || (pt >= 0 && input.Substring(pt).Trim().Length == 0); + if (!atEnd) { + return false; + } + + // A command is never multi-line, so always try to execute something which looks like a command + if (input.StartsWith(_commandPrefix)) { + return true; + } + + return Evaluator.CanExecuteText(input); + } + + #endregion + + #region Private Methods + + private Dispatcher Dispatcher { + get { + return ((FrameworkElement)CurrentView).Dispatcher; + } + } + + private int CurrentInsertionPoint { + get { return Caret.Position.BufferPosition.Position; } + } + + private bool CaretAtEnd { + get { return CurrentInsertionPoint == CurrentSnapshot.Length; } + } + + private string ActiveInput { + get { + var s = CurrentSnapshot; + var p = _inputPoint.GetPosition(s); + return s.GetText(p, s.Length - p); + } + } + + private int RelativeInsertionPoint { + get { + var p = _inputPoint.GetPosition(CurrentSnapshot); + return CurrentInsertionPoint - p; + } + } + + private IIntellisenseSessionStack SessionStack { + get { + if (_sessionStack == null) { + IIntellisenseSessionStackMapService stackMapService = ComponentModel.GetService(); + _sessionStack = stackMapService.GetStackForTextView(CurrentView); + } + + return _sessionStack; + } + } + + private bool IsCaretProtected { + get { + return _readOnlyRegions[0] != null && + Caret.Position.BufferPosition.Position < _readOnlyRegions[0].Span.GetStartPoint(CurrentView.TextBuffer.CurrentSnapshot).Position; + } + } + + private bool CaretInFirstInputLine { + get { + var pos = Caret.Position.BufferPosition.Position; + var line = Caret.Position.BufferPosition.GetContainingLine().LineNumber; + var currentInput = MakeInputSpan(); + if (currentInput == null) { + return false; + } + var span = currentInput.GetSpan(CurrentSnapshot); + return (span.Start.GetContainingLine().LineNumber == line && + pos >= span.Start.Position); + } + } + + private bool CaretInLastInputLine { + get { + var pos = Caret.Position.BufferPosition.Position; + var line = Caret.Position.BufferPosition.GetContainingLine().LineNumber; + var currentInput = MakeInputSpan(); + if (currentInput == null) { + return false; + } + var span = currentInput.GetSpan(CurrentSnapshot); + return (span.End.GetContainingLine().LineNumber == line); + } + } + + public bool CaretInInputRegion { + get { + var currentInput = MakeInputSpan(false, null); + if (currentInput == null) { + return false; + } + var span = currentInput.GetSpan(CurrentSnapshot); + return Caret.Position.BufferPosition.Position >= span.Start.Position && Caret.Position.BufferPosition.Position <= span.End.Position; + } + } + + private string InputTextToCaret { + get { + var s = CurrentSnapshot; + var p = _inputPoint.GetPosition(s); + var sl = Caret.Position.BufferPosition.Position - p; + return s.GetText(p, sl); + } + } + + + private bool CheckAccess() { + return Dispatcher.CheckAccess(); + } + + public void ExecuteText() { + ExecuteText(null); + } + + private void AppendInput(string text) { + if (!CheckAccess()) { + Dispatcher.BeginInvoke(new Action(() => AppendInput(text))); + return; + } + + using (var edit = TextBuffer.CreateEdit()) { + edit.Insert(edit.Snapshot.Length, text); + edit.Apply(); + } + + Caret.EnsureVisible(); + } + + private void EnsureNewLine() { + if (!CheckAccess()) { + Dispatcher.BeginInvoke(new Action(() => EnsureNewLine())); + return; + } + + AppendInput(_textViewHost.TextView.Options.GetNewLineCharacter()); + if (!CaretAtEnd) { + _editorOperations.MoveToEndOfDocument(false); + } + } + + private ITrackingSpan MakeInputSpan() { + return MakeInputSpan(false, null); + } + + private ITrackingSpan MakeInputSpan(bool excludeCrLf, ITextSnapshot snapshot) { + if (_isRunning || _inputPoint == null) { + return null; + } + + snapshot = snapshot ?? CurrentSnapshot; + int pos = _inputPoint.GetPosition(snapshot); + int len = snapshot.Length - pos; + + if (excludeCrLf && len > 1) { + // remove the line feed including any trailing linespace + var text = snapshot.GetText(pos, len); + var endTrimmed = text.TrimEnd(' '); + string newLine = _textViewHost.TextView.Options.GetNewLineCharacter(); + if (endTrimmed.Length > newLine.Length && String.Compare(endTrimmed, endTrimmed.Length - newLine.Length, newLine, 0, newLine.Length) == 0) { + len -= newLine.Length + (text.Length - endTrimmed.Length); + } + } + return snapshot.CreateTrackingSpan(pos, len, SpanTrackingMode.EdgeExclusive); + } + + private void Write(string text) { + PerformWrite(() => { + if (_outputPoint == null || !_showOutput) { + return; + } + + using (var edit = TextBuffer.CreateEdit()) { + int insertPos = _outputPoint.GetPosition(CurrentSnapshot); + edit.Insert(insertPos, text); + edit.Apply(); + } + Caret.EnsureVisible(); + }); + } + + private void WriteOutput(object sender, Output output) { + if (!CheckAccess()) { + Dispatcher.BeginInvoke(new Action(() => WriteOutput(sender, output))); + return; + } + + if (output.Object != null) { + if (TryShowObject(output.Object, output.Text)) { + return; + } + WriteLine(output.Text); + } else { + Write(output.Text); + } + } + + private string ReadInput() { + // shouldn't be called on the UI thread because we'll hang + Debug.Assert(!CheckAccess()); + + bool wasRunning = _isRunning; + // TODO: What do we do if we weren't running? + if (_isRunning) { + RemoveProtection(); + _isRunning = false; + } + + Dispatcher.Invoke(new Action(() => { + _outputPoint = CurrentSnapshot.CreateTrackingPoint(CurrentSnapshot.Length, PointTrackingMode.Positive); + _inputPoint = CurrentSnapshot.CreateTrackingPoint(CurrentSnapshot.Length, PointTrackingMode.Negative); + })); + + _inputEvent.WaitOne(); + + if (wasRunning) { + ApplyProtection(); + _isRunning = true; + } + + return _textViewHost.TextView.Options.GetNewLineCharacter(); + } + + private List GetInputSpans() { + var currentInput = MakeInputSpan(); + if (currentInput == null) { + return _inputSpans; + } + var result = new List(_inputSpans); + result.Add(currentInput); + return result; + } + + private SnapshotSpan? GetInputSpanContainingCaret() { + var bufferPos = Caret.Position.BufferPosition; + var pos = bufferPos.Position; + var s = bufferPos.Snapshot; + foreach (var inputSpan in GetInputSpans()) { + var span = inputSpan.GetSpan(s); + if (pos >= span.Start.Position && pos <= span.End.Position) { + return span; + } + } + return null; + } + + private bool ProcessPendingInput() { + if (!CheckAccess()) { + return (bool)(Dispatcher.Invoke(new Action(() => ProcessPendingInput()))); + } + + while (_pendingInput.Count > 0) { + var line = _pendingInput[0]; + _pendingInput.RemoveAt(0); + AppendInput(line.Text); + _editorOperations.MoveToEndOfDocument(false); + if (line.HasNewline) { + if (TryExecuteInput()) { + return true; + } + EnsureNewLine(); + } + } + return false; + } + + private bool RemoveProtection() { + bool wasProtected = false; + using (var edit = TextBuffer.CreateReadOnlyRegionEdit()) { + foreach (var region in _readOnlyRegions) { + if (region != null) { + edit.RemoveReadOnlyRegion(region); + wasProtected = true; + } + } + edit.Apply(); + _readOnlyRegions[0] = _readOnlyRegions[1] = null; + } + + return wasProtected; + } + + private void ApplyProtection() { + SpanTrackingMode trackingMode; + EdgeInsertionMode insertionMode; + int end; + if (_isRunning) { + trackingMode = SpanTrackingMode.EdgeInclusive; + insertionMode = EdgeInsertionMode.Deny; + end = CurrentSnapshot.Length; + } else { + trackingMode = SpanTrackingMode.EdgeExclusive; + insertionMode = EdgeInsertionMode.Allow; + end = _inputPoint.GetPosition(CurrentSnapshot); + } + + using (var edit = TextBuffer.CreateReadOnlyRegionEdit()) { + IReadOnlyRegion region0 = edit.CreateReadOnlyRegion(new Span(0, end), trackingMode, insertionMode); + + // Create a second read-only region to prevent insert at start of buffer: + IReadOnlyRegion region1 = (end > 0) ? edit.CreateReadOnlyRegion(new Span(0, 0), SpanTrackingMode.EdgeExclusive, EdgeInsertionMode.Deny) : null; + + edit.Apply(); + _readOnlyRegions[0] = region0; + _readOnlyRegions[1] = region1; + } + } + + private bool ExecuteReplCommand(string commandLine) { + commandLine = commandLine.Trim(); + IReplCommand commandFn = null; + string args, command = null; + if (commandLine.Length == 0 || commandLine == "help") { + ShowReplHelp(); + return true; + } else if (commandLine.Substring(0, 1) == _commandPrefix) { // TODO ?? + // REPL-level comment; do nothing + return true; + } else { + command = commandLine.Split(' ')[0]; + args = commandLine.Substring(command.Length).Trim(); + commandFn = _commands.Find(x => x.Command == command); + } + + if (commandFn == null) { + WriteLine(String.Format("Unknown command '{0}', use \"{1}help\" for help", command, _commandPrefix)); + return false; + } + try { + // commandFn is either an Action or Action + commandFn.Execute(this, args); + return true; + } catch (Exception e) { + WriteLine(Evaluator.FormatException(new ObjectHandle(e))); + return false; + } + } + + public bool TryExecuteInput() { + bool tryIt = CanExecuteInput(); + if (tryIt) { + ExecuteText(); + } + return tryIt; + } + + private void PerformWrite(Action action) { + if (!CheckAccess()) { + Dispatcher.Invoke(new Action(() => PerformWrite(action))); + return; + } + + bool wasProtected = RemoveProtection(); + try { + action(); + } finally { + if (wasProtected) { + ApplyProtection(); + } + } + } + + /// + /// Execute and then call the callback function with the result text. + /// + /// + internal void ExecuteText(Action processResult) { + PerformWrite(() => { + // Ensure that the REPL doesn't try to execute if it is already + // executing. If this invariant can no longer be maintained more of + // the code in this method will need to be bullet-proofed + if (_isRunning) { + return; + } + + var text = ActiveInput; + var span = CreateInputSpan(text); + + _isRunning = true; + Debug.Assert(_processResult == null); + _processResult = processResult; + + // Following method assumes that _isRunning will be cleared before + // the following method is called again. + StartCursorTimer(); + + _sw = Stopwatch.StartNew(); + if (text.Trim().Length == 0) { + // Special case to avoid round-trip when remoting + FinishExecute(true, null); + } else if (text.StartsWith(_commandPrefix)) { + _history.Last.Command = true; + var status = ExecuteReplCommand(text.Substring(_commandPrefix.Length)); + FinishExecute(status, null); + } else if (!Evaluator.ExecuteText(ActiveInput, FinishExecute)) { + _inputSpans.Add(span); + FinishExecute(false, null); + } else { + _inputSpans.Add(span); + } + }); + } + + private ITrackingSpan CreateInputSpan(string text) { + CurrentView.Selection.Clear(); + var s = CurrentSnapshot; + _outputPoint = s.CreateTrackingPoint(s.Length, PointTrackingMode.Positive); + var span = MakeInputSpan(true, s); + + if (text.Length > 0) { + _history.Add(text.TrimEnd()); + } + return span; + } + + private void FinishExecute(bool success, ObjectHandle exception) { + PerformWrite(() => { + _sw.Stop(); + var handler = ExecutionFinished; + if (handler != null) { + handler(); + } + if (_history.Last != null) { + _history.Last.Duration = _sw.Elapsed.Seconds; + } + if (!success) { + if (exception != null) { + AddFileHyperlink(exception); + } + if (_history.Last != null) { + _history.Last.Failed = true; + } + _errorInputs.Add(_inputSpans.Count); + } + PrepareForInput(); + var processResult = _processResult; + if (processResult != null) { + _processResult = null; + var spans = GetAllSpans(CurrentSnapshot); + var lastSpan = spans[spans.Length - 2]; + processResult(lastSpan); + } + }); + } + + private static SnapshotSpan? GetSpanFromInput(ITextSnapshot snapshot, int line, ITrackingSpan curInput) { + var span = curInput.GetSpan(snapshot); + var start = span.Start; + var startLine = start.GetContainingLine(); + + if (startLine.LineNumber == line) { + // we are the 1st line of a user entered code block + return span.Intersection(snapshot.GetLineFromPosition(span.Start.Position).Extent); + } else if (startLine.LineNumber < line) { + // we may be part of a multi-line block + + var end = span.End; + var endLine = end.GetContainingLine(); + + if (endLine.LineNumber == line) { + // we are the last line of a user entered code block + return span.Overlap(snapshot.GetLineFromPosition(end.Position).Extent); + } else if (endLine.LineNumber > line) { + // we are in the middle of a user entered block, return the entire line + return snapshot.GetLineFromLineNumber(line).Extent; + } + } + return null; + } + + + public void ExecuteOrPasteSelected() { + if (CaretInInputRegion) { + EnsureNewLine(); + ExecuteText(); + } else { + string input = GetCurrentSelectedInput(TextBuffer.CurrentSnapshot); + if (input != null) { + _editorOperations.MoveToEndOfDocument(false); + PasteText(input); + } + } + } + + private string GetCurrentSelectedInput(ITextSnapshot snapshot) { + List inputs = GetInputSpans(snapshot); + + var curPosition = Caret.Position.BufferPosition.Position; + foreach (var input in inputs) { + if (curPosition >= input.GetStartPoint(snapshot).Position && + curPosition <= input.GetEndPoint(snapshot).Position) { + return input.GetText(snapshot); + } + } + + return null; + } + + private ReplSpan[] GetAllSpans(ITextSnapshot s) { + // For thread safety, clone _inputSpans on the UI thread. + // TODO: review the rest of the intellisense interface for threadsafety + List inputs = GetInputSpans(s); + + SnapshotSpan? last = null; + var result = new List(); + for (int i = 0; i < inputs.Count; i++) { + var input = inputs[i].GetSpan(s); + if (last != null) { + var eoo = input.Start - 1 - (_evaluator.DisplayPromptInMargin ? 0 : _prompt.Length); + var start = eoo; + var end = last.Value.End; + end += Math.Min(2, end.Snapshot.Length - end.Position); + if (start.Position > end.Position) { + start = end; + end = eoo; + } + var output = new SnapshotSpan(start, end); + bool wasCommand = last.Value.GetText().StartsWith(_commandPrefix); // TODO: get less text? + bool wasException = _errorInputs.Contains(i); + result.Add(new ReplSpan(wasCommand, wasException, last.Value, output)); + } + last = input; + } + if (last != null) { + bool finalWasCommand = last.Value.GetText().StartsWith(_commandPrefix); + result.Add(new ReplSpan(finalWasCommand, false, last.Value, null)); + } + + return result.ToArray(); + } + + private List GetInputSpans(ITextSnapshot s) { + List inputs = null; + + UIThread(() => { + inputs = new List(_inputSpans); + var currentInput = MakeInputSpan(false, s); + if (currentInput != null) { + inputs.Add(currentInput); + } + }); + return inputs; + } + + private void SetupOutput() { + _outputPoint = null; + _showOutput = true; + } + + private bool TryShowObject(object obj, string textRepr) { + Image image = null; + var imageSource = obj as ImageSource; + if (imageSource != null) { + image = new Image(); + image.Source = imageSource; + } + return false; + } + + private void ShowReplHelp() { + var cmdnames = new List(_commands.Where(x => x.Command != null)); + cmdnames.Sort((x, y) => String.Compare(x.Command, y.Command)); + + const string helpFmt = " {0,-16} {1}"; + WriteLine(string.Format(helpFmt, "help", "Show a list of REPL commands")); + + foreach (var cmd in cmdnames) { + WriteLine(string.Format(helpFmt, cmd.Command, cmd.Description)); + } + } + + private void AddFileHyperlink(ObjectHandle exception) { + // TODO: + } + + private void PrepareForInput() { + Evaluator.FlushOutput(); + + int saved = CurrentSnapshot.Length; + if (!_evaluator.DisplayPromptInMargin) { + using (var edit = TextBuffer.CreateEdit()) { + // remove any leading white space which may have been inserted by auto-indent support + var containingLine = Caret.Position.BufferPosition.GetContainingLine(); + if (Caret.Position.BufferPosition.Position > containingLine.Start) { + int length = Caret.Position.BufferPosition.Position - containingLine.Start.Position; + edit.Delete(new Span(containingLine.Start.Position, length)); + saved -= length; + } + edit.Insert(saved, _prompt + " "); + edit.Apply(); + } + } + + Caret.EnsureVisible(); + + _outputPoint = CurrentSnapshot.CreateTrackingPoint(saved, PointTrackingMode.Positive); + _inputPoint = CurrentSnapshot.CreateTrackingPoint(CurrentSnapshot.Length, PointTrackingMode.Negative); + + ResetCursor(); + _isRunning = false; + _unCommittedInput = null; + ProcessPendingInput(); + + // we need to update margin prompt after the new _inputPoint is set: + if (_evaluator.DisplayPromptInMargin) { + var promptChanged = PromptChanged; + if (promptChanged != null) { + promptChanged(new SnapshotSpan(CurrentSnapshot, new Span(CurrentSnapshot.Length, 0))); + } + } + } + + private void ResetCursor() { + if (_executionTimer != null) { + _executionTimer.Stop(); + } + if (_oldCursor != null) { + ((ContentControl)CurrentView).Cursor = _oldCursor; + } + /*if (_oldCaretBrush != null) { + CurrentView.Caret.RegularBrush = _oldCaretBrush; + }*/ + + _oldCursor = null; + //_oldCaretBrush = null; + _executionTimer = null; + } + + private void StartCursorTimer() { + // Save the old value of the caret brush so it can be restored + // after execution has finished + //_oldCaretBrush = CurrentView.Caret.RegularBrush; + + // Set the caret's brush to transparent so it isn't shown blinking + // while code is executing in the REPL + //CurrentView.Caret.RegularBrush = Brushes.Transparent; + + var timer = new DispatcherTimer(); + timer.Tick += SetRunningCursor; + timer.Interval = new TimeSpan(0, 0, 0, 0, 250); + _executionTimer = timer; + timer.Start(); + } + + private void SetRunningCursor(object sender, EventArgs e) { + var view = (ContentControl)CurrentView; + + // Save the old value of the cursor so it can be restored + // after execution has finished + _oldCursor = view.Cursor; + + // TODO: Design work to come up with the correct cursor to use + // Set the repl's cursor to the "executing" cursor + view.Cursor = Cursors.Wait; + + // Stop the timeer so it doesn't fire again + _executionTimer.Stop(); + } + + private void StoreUncommittedInput() { + if (_unCommittedInput == null && !string.IsNullOrEmpty(ActiveInput)) { + _unCommittedInput = ActiveInput; + } + } + + private void InsertUncommittedInput() { + if (_unCommittedInput != null) { + using (var edit = TextBuffer.CreateEdit()) { + var snapshot = CurrentSnapshot; + var span = MakeInputSpan().GetSpan(snapshot); + edit.Replace(span, _unCommittedInput); + edit.Apply(); + } + _unCommittedInput = null; + } + } + + private void StartEvaluator() { + Evaluator.SetIO(WriteOutput, ReadInput); + Evaluator.Start(); + } + + /// + /// Clear the current input region and move the caret to the right + /// place for entering new text + /// + private void ClearCurrentInput() { + // if there's an intellisense session we cancel that, otherwise we clear the current input. + if (!((IIntellisenseCommandTarget)this.SessionStack).ExecuteKeyboardCommand(IntellisenseKeyboardCommand.Escape)) { + var currentInput = MakeInputSpan(); + if (currentInput != null) { + using (var edit = TextBuffer.CreateEdit()) { + var span = currentInput.GetSpan(CurrentSnapshot); + edit.Delete(span); + edit.Apply(); + } + } + _editorOperations.MoveToEndOfDocument(false); + _unCommittedInput = null; + } + } + + private void SelectHistoryItem(string text) { + string newLine = _textViewHost.TextView.Options.GetNewLineCharacter(); + while (text.EndsWith(newLine)) { + text = text.Substring(0, text.Length - newLine.Length); + } + + var position = Caret.Position.BufferPosition.Position; + using (var edit = TextBuffer.CreateEdit()) { + var snapshot = CurrentSnapshot; + var span = MakeInputSpan().GetSpan(snapshot); + edit.Replace(span, text); + edit.Apply(); + } + + //Caret.MoveTo(Math.Min(position, CurrentSnapshot.Length)); + } + + private T UIThread(Func func) { + if (!CheckAccess()) { + return (T)Dispatcher.Invoke(func); + } + return func(); + } + + private void UIThread(Action action) { + if (!CheckAccess()) { + Dispatcher.Invoke(action); + return; + } + action(); + } + + /// + /// Pump events until condition returns true or time runs out. + /// TODO: move to a utilities class. + /// + private static bool PumpEvents(Func condition, int msToWait) { + var d = Dispatcher.CurrentDispatcher; + var end = DateTime.Now.Ticks + (msToWait * 10000); + while (!condition() && DateTime.Now.Ticks < end && !d.HasShutdownStarted) { + DoEvents(); + } + return condition(); + } + + private static void DoEvents() { + var frame = new DispatcherFrame(); + Dispatcher.CurrentDispatcher.BeginInvoke( + DispatcherPriority.Background, + new Action(f => f.Continue = false), + frame + ); + Dispatcher.PushFrame(frame); + } + + private static List SplitLines(string text) { + List lines = new List(); + int curStart = 0; + for (int i = 0; i < text.Length; i++) { + if (text[i] == '\r') { + if (i < text.Length - 1 && text[i + 1] == '\n') { + lines.Add(new PendingInput(text.Substring(curStart, i - curStart), true)); + curStart = i + 2; + i++; // skip \n + } else { + lines.Add(new PendingInput(text.Substring(curStart, i - curStart), true)); + curStart = i + 1; + } + } else if (text[i] == '\n') { + lines.Add(new PendingInput(text.Substring(curStart, i - curStart), true)); + curStart = i + 1; + } + } + if (curStart < text.Length) { + lines.Add(new PendingInput(text.Substring(curStart, text.Length - curStart), false)); + } + + return lines; + } + + private void AddScopeBox(ToolBar/*!*/ tb, IMultipleScopeEvaluator/*!*/ multiScopeEval) { + var label = new Label(); + label.Content = "File scope: "; + label.ToolTip = "Selects the current file scope that the REPL is executing against."; + + var comboBox = _scopebox = new ComboBox(); + comboBox.MouseEnter += new MouseEventHandler(ComboBoxMouseEnter); + + tb.Items.Add(label); + tb.Items.Add(comboBox); + UpdateScopeList(this, EventArgs.Empty); + multiScopeEval.AvailableScopesChanged += new EventHandler(UpdateScopeList); + multiScopeEval.CurrentScopeChanged += new EventHandler(UpdateScopeList); + + comboBox.SelectionChanged += (sender, args) => { + if (!_updatingList && comboBox.SelectedItem != null) { + StoreUncommittedInput(); + WriteLine(String.Format("Current scope changed to {0}", comboBox.SelectedItem)); + multiScopeEval.SetScope((string)comboBox.SelectedItem); + InsertUncommittedInput(); + } + }; + } + + void ComboBoxMouseEnter(object sender, MouseEventArgs e) { + UpdateScopeList(sender, e); + } + + [ThreadStatic] + private static bool _updatingList; + + private void UpdateScopeList(object sender, EventArgs e) { + if (!CheckAccess()) { + Dispatcher.BeginInvoke(new Action(() => UpdateScopeList(sender, e))); + return; + } + + string currentScope = ((IMultipleScopeEvaluator)Evaluator).CurrentScopeName; + _updatingList = true; + _scopebox.Items.Clear(); + int index = 0; + bool found = false; + foreach (var scope in ((IMultipleScopeEvaluator)Evaluator).GetAvailableScopes()) { + _scopebox.Items.Add(scope); + if (!found && scope == currentScope) { + found = true; + } else if (!found) { + index++; + } + } + if (found) { + _scopebox.SelectedIndex = index; + } + _updatingList = false; + } + + private void AddToolBarButton(ToolBar tb, IReplCommand command) { + if (command.ButtonContent != null) { + var button = new Button(); + button.Content = command.ButtonContent; + button.ToolTip = command.Description; + button.Click += (sender, args) => { + if (command.Command != null) { + // show the keyboard command to the user + ClearCurrentInput(); + PasteText(_commandPrefix + command.Command); + EnsureNewLine(); + ExecuteText(); + } else { + // no keyboard command, just execute it. + command.Execute(this, String.Empty); + } + }; + tb.Items.Add(button); + } + } + + private IEditorOperations GetEditorOperations(IWpfTextView textView) { + IEditorOperationsFactoryService factory = ComponentModel.GetService(); + return factory.GetEditorOperations(textView); + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ResizingAdorner.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ResizingAdorner.cs new file mode 100644 index 0000000000..c3a5471634 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/ResizingAdorner.cs @@ -0,0 +1,95 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Windows; +using System.Windows.Controls.Primitives; +using System.Windows.Documents; +using System.Windows.Input; +using System.Windows.Media; + +namespace Microsoft.IronStudio.Core.Repl { + internal class ResizingAdorner : Adorner { + private readonly VisualCollection _visualChildren; + private readonly Thumb _bottomRight; + + public ResizingAdorner(UIElement adornedElement) + : base(adornedElement) { + _visualChildren = new VisualCollection(this); + _bottomRight = BuildAdornerCorner(Cursors.SizeNWSE, HandleBottomRight); + } + + private Thumb BuildAdornerCorner(Cursor cursor, DragDeltaEventHandler dragHandler) { + var thumb = new Thumb(); + // TODO: this thumb should be styled to look like a dotted triangle, + // similar to the one you can see on the bottom right corner of + // Internet Explorer window + thumb.Cursor = cursor; + thumb.Height = thumb.Width = 10; + thumb.Opacity = 0.40; + thumb.Background = new SolidColorBrush(Colors.MediumBlue); + thumb.DragDelta += dragHandler; + thumb.DragStarted += (s, e) => { + var handler = ResizeStarted; + if (handler != null) { + handler(this, e); + } + }; + thumb.DragCompleted += (s, e) => { + var handler = ResizeCompleted; + if (handler != null) { + handler(this, e); + } + }; + _visualChildren.Add(thumb); + return thumb; + } + + private void HandleBottomRight(object sender, DragDeltaEventArgs eventArgs) { + var thumb = sender as Thumb; + var element = AdornedElement as FrameworkElement; + if (element == null || thumb == null) { + return; + } + + element.MaxWidth = Math.Max(element.MaxWidth + eventArgs.HorizontalChange, thumb.DesiredSize.Width); + element.MaxHeight = Math.Max(element.MaxHeight + eventArgs.VerticalChange, thumb.DesiredSize.Height); + var size = new Size(element.MaxWidth, element.MaxHeight); + AdornedElement.Measure(size); + } + + protected override Size ArrangeOverride(Size finalSize) { + var desiredWidth = AdornedElement.DesiredSize.Width; + var desiredHeight = AdornedElement.DesiredSize.Height; + var adornerWidth = DesiredSize.Width; + var adornerHeight = DesiredSize.Height; + + _bottomRight.Arrange(new Rect((desiredWidth - adornerWidth) / 2, + (desiredHeight - adornerHeight) / 2, adornerWidth, adornerHeight)); + + return finalSize; + } + + protected override int VisualChildrenCount { + get { return _visualChildren.Count; } + } + + protected override Visual GetVisualChild(int index) { + return _visualChildren[index]; + } + + public event RoutedEventHandler ResizeStarted; + public event RoutedEventHandler ResizeCompleted; + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Writer.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Writer.cs new file mode 100644 index 0000000000..e6830e14ce --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/Repl/Writer.cs @@ -0,0 +1,49 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Text; + +namespace Microsoft.IronStudio.Core.Repl { + internal class Writer : TextWriter { + private readonly Action _writer; + + internal Writer(Action writer) { + _writer = writer; + } + + public override void Write(char[] buffer, int index, int count) { + _writer(new string(buffer, index, count)); + } + + public override void Write(string value) { + _writer(value); + } + + public override void Write(char value) { + _writer(new String(value, 1)); + } + + public override Encoding Encoding { + get { return Encoding.UTF8; } + } + + public override object InitializeLifetimeService() { + return null; + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/SnapshotMultipleSpanTextContentProvider.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/SnapshotMultipleSpanTextContentProvider.cs new file mode 100644 index 0000000000..97a2b1f3f6 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/SnapshotMultipleSpanTextContentProvider.cs @@ -0,0 +1,60 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Text; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Hosting.Providers; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronStudio.Core { + internal class SnapshotMultipleSpanTextContentProvider : TextContentProvider, ISnapshotTextContentProvider { + private readonly NormalizedSnapshotSpanCollection _spans; + + internal SnapshotMultipleSpanTextContentProvider(NormalizedSnapshotSpanCollection spans) { + _spans = spans; + } + + internal static SourceUnit Make(ScriptEngine engine, NormalizedSnapshotSpanCollection spans) { + return Make(engine, spans, "None"); + } + + internal static SourceUnit Make(ScriptEngine engine, NormalizedSnapshotSpanCollection spans, string path) { + var textContent = new SnapshotMultipleSpanTextContentProvider(spans); + var languageContext = HostingHelpers.GetLanguageContext(engine); + return new SourceUnit(languageContext, textContent, path, SourceCodeKind.File); + } + + #region TextContentProvider + + public override SourceCodeReader GetReader() { + return new SourceCodeReader(new SnapshotMultipleSpanSourceCodeReader(_spans), Encoding.Default); + } + + #endregion + + #region ISnapshotTextContentProvider + + public ITextSnapshot Snapshot { + get { + if (_spans.Count > 0) { + return _spans[0].Snapshot; + } + return null; + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/SnapshotMultpleSpanSourceCodeReader.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/SnapshotMultpleSpanSourceCodeReader.cs new file mode 100644 index 0000000000..e0baa72951 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/SnapshotMultpleSpanSourceCodeReader.cs @@ -0,0 +1,126 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.IO; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronStudio.Core { + internal class SnapshotMultipleSpanSourceCodeReader : TextReader { + private readonly NormalizedSnapshotSpanCollection _spans; + private readonly SnapshotSpanSourceCodeReader[] _readers; + private int _index; + + internal SnapshotMultipleSpanSourceCodeReader(NormalizedSnapshotSpanCollection spans) { + _spans = spans; + _readers = spans.Select(s => new SnapshotSpanSourceCodeReader(s)).ToArray(); + _index = 0; + } + + #region TextReader + + public override void Close() { + foreach (var r in _readers) { + r.Close(); + } + base.Close(); + } + + public int EditorPosition { + get { return _readers[_index].Position; } + } + + public void MoveTo(int line, int column) { + Reset(); + + for (int i = 0; i < line; i++) { + ReadLine(); + } + for (int i = 0; i < column; i++) { + Read(); + } + } + + public override int Peek() { + while (_index < _readers.Length) { + var c = _readers[_index].Peek(); + if (c != -1) { + return c; + } + _index++; + } + return -1; + } + + public override int Read() { + while (_index < _readers.Length) { + var c = _readers[_index].Read(); + if (c != -1) { + return c; + } + _index++; + } + return -1; + } + + public override int Read(char[] buffer, int index, int count) { + int read = 0; + while (_index < _readers.Length && count > 0) { + read += _readers[_index].Read(buffer, index + read, count - read); + _index++; + } + return read; + } + + public override string ReadLine() { + var text = new StringBuilder(); + while (_index <= _readers.Length) { + var c = Read(); + if (c == -1) { + break; + } + if (c == '\r' || c == '\n') { + if (Peek() == '\n') { + Read(); + } + break; + } + text.Append(c); + } + if (_index == _readers.Length && text.Length == 0) { + return null; + } + return text.ToString(); + } + + public override string ReadToEnd() { + var result = new StringBuilder(); + foreach (var reader in _readers) { + result.Append(reader.ReadToEnd()); + } + _index = _readers.Length - 1; + return result.ToString(); + } + + #endregion + + internal void Reset() { + foreach (var reader in _readers) { + reader.Reset(); + } + _index = 0; + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/SnapshotSpanSourceCodeReader.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/SnapshotSpanSourceCodeReader.cs new file mode 100644 index 0000000000..8a8676b445 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/SnapshotSpanSourceCodeReader.cs @@ -0,0 +1,120 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.IO; +using System.Text; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronStudio.Core { + internal partial class SnapshotSpanSourceCodeReader : TextReader { + private readonly SnapshotSpan _span; + private ITextSnapshot _snapshot; + private int _position; + private string _buffer; + private Span? _bufferSpan; + private const int BufferSize = 1024; + + internal SnapshotSpanSourceCodeReader(SnapshotSpan span) { + _span = span; + _snapshot = span.Snapshot; + _position = span.Start.Position; + } + + #region TextReader + + public override void Close() { + Dispose(true); + } + + protected override void Dispose(bool disposing) { + _snapshot = null; + _position = 0; + } + + public override int Peek() { + CheckDisposed(); + if (_position == End) { + return -1; + } + return _snapshot[_position]; + } + + public override int Read() { + CheckDisposed(); + + if (_position == End) { + return -1; + } else if (_bufferSpan == null || !(_position >= _bufferSpan.Value.Start && _position < _bufferSpan.Value.End)) { + int bufferLength = Math.Min(BufferSize, _snapshot.Length - _position); + _buffer = _snapshot.GetText(_position, bufferLength); + _bufferSpan = new Span(_position, bufferLength); + } + + return _buffer[_position++ - _bufferSpan.Value.Start]; + } + + public override int Read(char[] buffer, int index, int count) { + int length = End - _position; + if (length > 0) { + length = System.Math.Min(length, count); + _snapshot.CopyTo(_position, buffer, index, length); + _position += length; + } + return length; + } + + public override string ReadLine() { + CheckDisposed(); + + if (_position == _snapshot.Length) { + return null; + } + + var line = _snapshot.GetLineFromPosition(_position); + _position = line.End.Position; + + return line.GetText(); + } + + public override string ReadToEnd() { + CheckDisposed(); + int length = End - _position; + var text = _snapshot.GetText(_position, length); + _position = End; + return text; + } + + #endregion + + internal int Position { + get { return _position; } + } + + internal void Reset() { + CheckDisposed(); + _position = _span.Start.Position; + } + + private void CheckDisposed() { + if (_snapshot == null) { + throw new ObjectDisposedException("This SnapshotSpanSourceCodeReader has been closed"); + } + } + + private int End { + get { return _span.End.Position; } + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/SnapshotSpanTextContentProvider.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/SnapshotSpanTextContentProvider.cs new file mode 100644 index 0000000000..92ad5a56c9 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/SnapshotSpanTextContentProvider.cs @@ -0,0 +1,47 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Text; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Hosting.Providers; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronStudio.Core { + internal class SnapshotSpanTextContentProvider : TextContentProvider, ISnapshotTextContentProvider { + private readonly SnapshotSpan _span; + + internal SnapshotSpanTextContentProvider(SnapshotSpan span) { + _span = span; + } + + #region TextContentProvider + + public override SourceCodeReader GetReader() { + return new SourceCodeReader(new SnapshotSpanSourceCodeReader(_span), Encoding.Default); + } + + #endregion + + #region ISnapshotTextContentProvider + + public ITextSnapshot Snapshot { + get { + return _span.Snapshot; + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/SnapshotTextContentProvider.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/SnapshotTextContentProvider.cs new file mode 100644 index 0000000000..fa570a6782 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/SnapshotTextContentProvider.cs @@ -0,0 +1,59 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Text; +using Microsoft.IronStudio.Core; +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Hosting.Providers; +using Microsoft.VisualStudio.Text; + +namespace Microsoft.IronStudio.Library { + public class SnapshotTextContentProvider : TextContentProvider, ISnapshotTextContentProvider { + private readonly ITextSnapshot _snapshot; + + internal SnapshotTextContentProvider(ITextSnapshot snapshot) { + _snapshot = snapshot; + } + + public static SourceUnit Make(ScriptEngine engine, ITextSnapshot snapshot) { + return Make(engine, snapshot, "None"); + } + + public static SourceUnit Make(ScriptEngine engine, ITextSnapshot snapshot, string path) { + var textContent = new SnapshotTextContentProvider(snapshot); + var languageContext = HostingHelpers.GetLanguageContext(engine); + return new SourceUnit(languageContext, textContent, path, SourceCodeKind.File); + } + + #region TextContentProvider + + public override SourceCodeReader GetReader() { + var span = new SnapshotSpan(_snapshot, 0, _snapshot.Length); + return new SourceCodeReader(new SnapshotSpanSourceCodeReader(span), Encoding.Default); + } + + #endregion + + #region ISnapshotTextContentProvider + + public ITextSnapshot Snapshot { + get { + return _snapshot; + } + } + + #endregion + } +} diff --git a/Tools/IronStudio/IronStudioCore/IronStudioCore/TokenCache.cs b/Tools/IronStudio/IronStudioCore/IronStudioCore/TokenCache.cs new file mode 100644 index 0000000000..ecdbd0a413 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/IronStudioCore/TokenCache.cs @@ -0,0 +1,135 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; + +using Microsoft.Scripting; +using Microsoft.Scripting.Hosting; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Text.Tagging; +using System.Diagnostics.Contracts; +using System.Diagnostics; +using System.Collections; +using System; +using System.Text; +using Microsoft.Scripting.Utils; + +namespace Microsoft.IronStudio.Core { + [DebuggerDisplay("{GetDebugView(),nq}")] + internal struct LineTokenization : ITag { + public readonly TokenInfo[] Tokens; + public readonly object State; + + public LineTokenization(TokenInfo[] tokens, object state) { + Tokens = tokens; + State = state; + } + + internal string GetDebugView() { + StringBuilder sb = new StringBuilder(); + if (State != null) { + sb.Append(State != null ? "S " : " "); + } + if (Tokens != null) { + for (int i = 0; i < Tokens.Length; i++) { + sb.Append('['); + sb.Append(Tokens[i].Category); + sb.Append(']'); + } + } + return sb.ToString(); + } + } + + internal class TokenCache { + private LineTokenization[] _map; + + internal TokenCache() { + _map = null; + } + + /// + /// Looks for the first cached tokenization preceding the given line. + /// Returns the line we have a tokenization for or minLine - 1 if there is none. + /// + internal int IndexOfPreviousTokenization(int line, int minLine, out LineTokenization tokenization) { + Debug.Assert(line >= 0); + Debug.Assert(_map != null); + + line--; + while (line >= minLine) { + if (_map[line].Tokens != null) { + tokenization = _map[line]; + return line; + } + line--; + } + tokenization = default(LineTokenization); + return minLine - 1; + } + + internal bool TryGetTokenization(int line, out LineTokenization tokenization) { + Debug.Assert(line >= 0); + Debug.Assert(_map != null); + + if (_map[line].Tokens != null) { + tokenization = _map[line]; + return true; + } else { + tokenization = default(LineTokenization); + return false; + } + } + + internal LineTokenization this[int line] { + get { + return _map[line]; + } + set { + _map[line] = value; + } + } + + internal void Clear() { + _map = null; + } + + internal void EnsureCapacity(int capacity) { + if (_map == null) { + _map = new LineTokenization[capacity]; + } else if (_map.Length < capacity) { + Array.Resize(ref _map, Math.Max(capacity, (_map.Length + 1) * 2)); + } + } + + internal void DeleteLines(int index, int count) { + Debug.Assert(_map != null); + Debug.Assert(index <= _map.Length - count); + + Array.Copy(_map, index + count, _map, index, _map.Length - index - count); + for (int i = 0; i < count; i++) { + _map[_map.Length - i - 1] = default(LineTokenization); + } + } + + internal void InsertLines(int index, int count) { + Debug.Assert(_map != null); + + Array.Copy(_map, index, _map, index + count, _map.Length - index - count); + for (int i = 0; i < count; i++) { + _map[index + i] = default(LineTokenization); + } + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/Properties/AssemblyInfo.cs b/Tools/IronStudio/IronStudioCore/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..d526c882e5 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("IronStudioCore")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft")] +[assembly: AssemblyProduct("IronStudioCore")] +[assembly: AssemblyCopyright("Copyright © Microsoft 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("9ad55b99-67e5-4025-9d4b-07e231e183d1")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tools/IronStudio/IronStudioCore/Resources/CancelEvaluation.gif b/Tools/IronStudio/IronStudioCore/Resources/CancelEvaluation.gif new file mode 100644 index 0000000000..81200b6166 Binary files /dev/null and b/Tools/IronStudio/IronStudioCore/Resources/CancelEvaluation.gif differ diff --git a/Tools/IronStudio/IronStudioCore/Resources/ResetSession.gif b/Tools/IronStudio/IronStudioCore/Resources/ResetSession.gif new file mode 100644 index 0000000000..1cdd72f0a8 Binary files /dev/null and b/Tools/IronStudio/IronStudioCore/Resources/ResetSession.gif differ diff --git a/Tools/IronStudio/IronStudioCore/Resources/clearscr.gif b/Tools/IronStudio/IronStudioCore/Resources/clearscr.gif new file mode 100644 index 0000000000..e558bba4ff Binary files /dev/null and b/Tools/IronStudio/IronStudioCore/Resources/clearscr.gif differ diff --git a/Tools/IronStudio/IronStudioCore/Scripting/CollectingErrorSink.cs b/Tools/IronStudio/IronStudioCore/Scripting/CollectingErrorSink.cs new file mode 100644 index 0000000000..e05a9af95e --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/Scripting/CollectingErrorSink.cs @@ -0,0 +1,45 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.Collections.Generic; + +namespace Microsoft.Scripting.Library { + public class CollectingErrorSink : ErrorSink { + public List Errors; + public List Warnings; + + public CollectingErrorSink() { + Errors = new List(); + Warnings = new List(); + } + + public override void Add(SourceUnit source, string message, SourceSpan span, int errorCode, Severity severity) { + span = Add(message, span, severity); + } + + private SourceSpan Add(string message, SourceSpan span, Severity severity) { + if (severity == Severity.Warning) { + Warnings.Add(new ErrorResult(message, span)); + } else if (severity == Severity.FatalError || severity == Severity.Error) { + Errors.Add(new ErrorResult(message, span)); + } + return span; + } + + public override void Add(string message, string path, string code, string line, SourceSpan span, int errorCode, Severity severity) { + Add(message, span, severity); + } + } + +} diff --git a/Tools/IronStudio/IronStudioCore/Scripting/ErrorResult.cs b/Tools/IronStudio/IronStudioCore/Scripting/ErrorResult.cs new file mode 100644 index 0000000000..b832d35d69 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/Scripting/ErrorResult.cs @@ -0,0 +1,32 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ +namespace Microsoft.Scripting { + public struct ErrorResult { + private readonly string _message; + private readonly SourceSpan _span; + + public ErrorResult(string message, SourceSpan span) { + _message = message; + _span = span; + } + + public string Message { + get { return _message; } + } + + public SourceSpan Span { + get { return _span; } + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/Scripting/FileTextContentProvider.cs b/Tools/IronStudio/IronStudioCore/Scripting/FileTextContentProvider.cs new file mode 100644 index 0000000000..b441da2c0b --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/Scripting/FileTextContentProvider.cs @@ -0,0 +1,60 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.IO; +using System.Text; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Hosting.Providers; +using Microsoft.Scripting.Runtime; + +namespace Microsoft.Scripting.Library { + public class FileTextContentProvider : TextContentProvider { + private readonly string _path; + + public FileTextContentProvider(string path) { + _path = path; + } + + public static SourceUnit Make(ScriptEngine engine, string path) { + var textContent = new FileTextContentProvider(path); + var languageContext = HostingHelpers.GetLanguageContext(engine); + return new SourceUnit(languageContext, textContent, path, SourceCodeKind.File); + } + + public static SourceUnit MakeEmpty(ScriptEngine engine) { + return MakeEmpty(engine, "None"); + } + + public static SourceUnit MakeEmpty(ScriptEngine engine, string path) { + var textContent = NullTextContentProvider.Null; + var languageContext = HostingHelpers.GetLanguageContext(engine); + return new SourceUnit(languageContext, textContent, path, SourceCodeKind.File); + } + + #region TextContentProvider + + public override SourceCodeReader GetReader() { + var stream = new FileStream(_path, FileMode.Open, FileAccess.Read); + return new SourceCodeReader(new StreamReader(stream), Encoding.Default); + } + + #endregion + + public string Path { + get { + return _path; + } + } + } +} diff --git a/Tools/IronStudio/IronStudioCore/Scripting/StringTextContentProvider.cs b/Tools/IronStudio/IronStudioCore/Scripting/StringTextContentProvider.cs new file mode 100644 index 0000000000..513ed297c3 --- /dev/null +++ b/Tools/IronStudio/IronStudioCore/Scripting/StringTextContentProvider.cs @@ -0,0 +1,47 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System.IO; +using System.Text; +using Microsoft.Scripting.Hosting; +using Microsoft.Scripting.Hosting.Providers; + +namespace Microsoft.Scripting.Library { + public class StringTextContentProvider : TextContentProvider { + private readonly string _text; + + public StringTextContentProvider(string text) { + _text = text; + } + + public static SourceUnit Make(ScriptEngine engine, string text, SourceCodeKind kind) { + return Make(engine, text, "None", kind); + } + + public static SourceUnit Make(ScriptEngine engine, string text, string path, SourceCodeKind kind) { + var textContent = new StringTextContentProvider(text); + var languageContext = HostingHelpers.GetLanguageContext(engine); + return new SourceUnit(languageContext, textContent, path, kind); + } + + #region TextContentProvider + + public override SourceCodeReader GetReader() { + return new SourceCodeReader(new StringReader(_text), Encoding.Default); + } + + #endregion + } + +} diff --git a/Tools/IronStudio/Key.snk b/Tools/IronStudio/Key.snk new file mode 100644 index 0000000000..3557d7b818 Binary files /dev/null and b/Tools/IronStudio/Key.snk differ diff --git a/Tools/IronStudio/Local.testsettings b/Tools/IronStudio/Local.testsettings new file mode 100644 index 0000000000..f24381b73b --- /dev/null +++ b/Tools/IronStudio/Local.testsettings @@ -0,0 +1,28 @@ + + + These are default test settings for a local test run. + + + + + + + + + + + + + +
+
+
+
+ + + + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/RemoteScriptFactory/Program.cs b/Tools/IronStudio/RemoteScriptFactory/Program.cs new file mode 100644 index 0000000000..2db62d020d --- /dev/null +++ b/Tools/IronStudio/RemoteScriptFactory/Program.cs @@ -0,0 +1,40 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Threading; +using Microsoft.IronStudio.RemoteEvaluation; + +namespace Microsoft.IronStudio { + class Program { + /// + /// Called when we start the remote server + /// + private static int Main(string[] args) { + ApartmentState state; + if (args.Length != 1 || !Enum.TryParse(args[0], out state)) { + Console.WriteLine("Expected no arguments"); + return 1; + } + + try { + RemoteScriptFactory.RunServer(state); + return 0; + } catch (Exception e) { + Console.WriteLine(e); + return 2; + } + } + } +} diff --git a/Tools/IronStudio/RemoteScriptFactory/Properties/AssemblyInfo.cs b/Tools/IronStudio/RemoteScriptFactory/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..6e7ea48eaf --- /dev/null +++ b/Tools/IronStudio/RemoteScriptFactory/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("RemoteHost")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft IT")] +[assembly: AssemblyProduct("RemoteHost")] +[assembly: AssemblyCopyright("Copyright © Microsoft IT 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("92c07f08-76c7-4770-9324-90ece3e54356")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tools/IronStudio/RemoteScriptFactory/RemoteScriptFactory.csproj b/Tools/IronStudio/RemoteScriptFactory/RemoteScriptFactory.csproj new file mode 100644 index 0000000000..75acd71ff7 --- /dev/null +++ b/Tools/IronStudio/RemoteScriptFactory/RemoteScriptFactory.csproj @@ -0,0 +1,82 @@ + + + + + Debug + AnyCPU + 8.0.30703 + 2.0 + {D10C905C-7F15-41DF-9FF9-CCE461F571FD} + Exe + Properties + Microsoft.IronStudio + RemoteScriptFactory + v4.0 + + + 512 + SAK + SAK + SAK + SAK + + + x86 + true + full + false + $(SolutionDir)..\bin\Debug + DEBUG;TRACE + prompt + 4 + + + x86 + pdbonly + true + $(SolutionDir)..\bin\Release + TRACE + prompt + 4 + + + + + + + + + + + + + + + + + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Dynamic + + + {02FF0909-F5AD-48CF-A86A-345E721B7E40} + Microsoft.Scripting + + + {843716AE-38B3-4723-963C-950DD06BC4B8} + IronStudioCore + + + + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/RemoteScriptFactory/RemoteScriptFactory.csproj.vspscc b/Tools/IronStudio/RemoteScriptFactory/RemoteScriptFactory.csproj.vspscc new file mode 100644 index 0000000000..feffdecaa4 --- /dev/null +++ b/Tools/IronStudio/RemoteScriptFactory/RemoteScriptFactory.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/Tools/IronStudio/RemoteScriptFactory/app.config b/Tools/IronStudio/RemoteScriptFactory/app.config new file mode 100644 index 0000000000..cb2586beb1 --- /dev/null +++ b/Tools/IronStudio/RemoteScriptFactory/app.config @@ -0,0 +1,3 @@ + + + diff --git a/Tools/IronStudio/UnitTests/CompletionContextTests.cs b/Tools/IronStudio/UnitTests/CompletionContextTests.cs new file mode 100644 index 0000000000..5ea13b872d --- /dev/null +++ b/Tools/IronStudio/UnitTests/CompletionContextTests.cs @@ -0,0 +1,138 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.IronPythonTools.Intellisense; +using Microsoft.VisualStudio.Utilities; +using Microsoft.Scripting.Hosting; +using IronPython.Hosting; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.IronPythonTools.Library; +using System.Threading; + +namespace UnitTests { + partial class Program { + public static IContentType PythonContentType = new MockContentType("Python", new IContentType[0]); + public static ScriptEngine PythonEngine = Python.CreateEngine(); + + [TestMethod] + public void Scenario_MemberCompletion() { + // TODO: Negative tests + // Import / from import tests + MemberCompletionTest(-1, "x = 2\r\nx.", "x."); + + // combining various partial expressions with previous expressions + var prefixes = new[] { "", "(", "a = ", "f(", "l[", "{", "if " }; + var exprs = new[] { "x[0].", "x(0).", "x", "x.y.", "f(x[2]).", "f(x, y).", "f({2:3}).", "f(a + b).", "f(a or b).", "{2:3}.", "f(x if False else y).", "(\r\nx\r\n)." }; + foreach (var prefix in prefixes) { + foreach (var expr in exprs) { + string test = prefix + expr; + Console.WriteLine(" -- {0}", test); + MemberCompletionTest(-1, test, expr); + } + } + + var sigs = new[] { + new { Expr = "f(", Param = 0, Function="f" } , + new { Expr = "f(1,", Param = 1, Function="f" }, + new { Expr = "f(1, 2,", Param = 2, Function="f" }, + new { Expr = "f(1, (1, 2),", Param = 2, Function="f" }, + new { Expr = "f(1, a + b,", Param = 2, Function="f" }, + new { Expr = "f(1, a or b,", Param = 2, Function="f" }, + new { Expr = "f(1, a if True else b,", Param = 2, Function="f" }, + new { Expr = "a.f(1, a if True else b,", Param = 2, Function="a.f" }, + new { Expr = "a().f(1, a if True else b,", Param = 2, Function="a().f" }, + new { Expr = "a(2, 3, 4).f(1, a if True else b,", Param = 2, Function="a(2, 3, 4).f" }, + new { Expr = "a(2, (3, 4), 4).f(1, a if True else b,", Param = 2, Function="a(2, (3, 4), 4).f" }, + }; + + foreach (var prefix in prefixes) { + foreach (var sig in sigs) { + var test = prefix + sig.Expr; + Console.WriteLine(" -- {0}", test); + SignatureTest(-1, test, sig.Function, sig.Param); + } + } + } + + [TestMethod] + public void Scenario_GotoDefinition() { + string code = @" +class C: + def fff(self): pass + +C().fff"; + + var emptyAnalysis = AnalyzeExpression(0, code); + AreEqual(emptyAnalysis.Expression, ""); + + for (int i = -1; i >= -3; i--) { + var analysis = AnalyzeExpression(i, code); + AreEqual(analysis.Expression, "C().fff"); + } + + var classAnalysis = AnalyzeExpression(-4, code); + AreEqual(classAnalysis.Expression, "C()"); + + var defAnalysis = AnalyzeExpression(code.IndexOf("def fff")+4, code); + AreEqual(defAnalysis.Expression, "fff"); + } + + private static ExpressionAnalysis AnalyzeExpression(int location, string sourceCode) { + if (location < 0) { + location = sourceCode.Length + location; + } + + var analyzer = new PythonAnalyzer(new MockDlrRuntimeHost(), new MockErrorProviderFactory()); + var buffer = new MockTextBuffer(sourceCode); + var textView = new MockTextView(buffer); + var item = analyzer.AnalyzeTextView(textView); + while (item.IsAnalyzed) { + Thread.Sleep(100); + } + + var snapshot = (MockTextSnapshot)buffer.CurrentSnapshot; + + return analyzer.AnalyzeExpression(snapshot, buffer, new MockTrackingSpan(snapshot, location, 0)); + } + + private static void MemberCompletionTest(int location, string sourceCode, string expectedExpression) { + if (location < 0) { + location = sourceCode.Length + location; + } + + var analyzer = new PythonAnalyzer(new MockDlrRuntimeHost(), new MockErrorProviderFactory()); + var buffer = new MockTextBuffer(sourceCode); + var snapshot = (MockTextSnapshot)buffer.CurrentSnapshot; + var context = analyzer.GetCompletions(snapshot, buffer, new MockTrackingSpan(snapshot, location, 1)); + AreEqual(context.Text, expectedExpression); + } + + private static void SignatureTest(int location, string sourceCode, string expectedExpression, int paramIndex) { + if (location < 0) { + location = sourceCode.Length + location; + } + + var analyzer = new PythonAnalyzer(new MockDlrRuntimeHost(), new MockErrorProviderFactory()); + var buffer = new MockTextBuffer(sourceCode); + var snapshot = (MockTextSnapshot)buffer.CurrentSnapshot; + var context = analyzer.GetSignatures(snapshot, buffer, new MockTrackingSpan(snapshot, location, 1)); + AreEqual(context.Text, expectedExpression); + AreEqual(context.ParameterIndex, paramIndex); + } + } +} diff --git a/Tools/IronStudio/UnitTests/MockClassificationType.cs b/Tools/IronStudio/UnitTests/MockClassificationType.cs new file mode 100644 index 0000000000..5509972880 --- /dev/null +++ b/Tools/IronStudio/UnitTests/MockClassificationType.cs @@ -0,0 +1,52 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Text.Classification; + +namespace UnitTests { + class MockClassificationType : IClassificationType { + private readonly string _name; + private readonly IClassificationType[] _bases; + + public MockClassificationType(string name, IClassificationType[] bases) { + _name = name; + _bases = bases; + } + + public IEnumerable BaseTypes { + get { return _bases; } + } + + public string Classification { + get { return _name; } + } + + public bool IsOfType(string type) { + if (type == _name) { + return true; + } + + foreach (var baseType in BaseTypes) { + if (baseType.IsOfType(type)) { + return true; + } + } + return false; + } + } +} diff --git a/Tools/IronStudio/UnitTests/MockClassificationTypeRegistryService.cs b/Tools/IronStudio/UnitTests/MockClassificationTypeRegistryService.cs new file mode 100644 index 0000000000..4ba88e175b --- /dev/null +++ b/Tools/IronStudio/UnitTests/MockClassificationTypeRegistryService.cs @@ -0,0 +1,51 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Language.StandardClassification; +using System.Reflection; + +namespace UnitTests { + class MockClassificationTypeRegistryService : IClassificationTypeRegistryService { + private static Dictionary _types = new Dictionary(); + + public MockClassificationTypeRegistryService() { + foreach (FieldInfo fi in typeof(PredefinedClassificationTypeNames).GetFields()) { + string name = (string)fi.GetValue(null); + _types[name] = new MockClassificationType(name, new IClassificationType[0]); + } + } + + public IClassificationType CreateClassificationType(string type, IEnumerable baseTypes) { + return _types[type] = new MockClassificationType(type, baseTypes.ToArray()); + } + + public IClassificationType CreateTransientClassificationType(params IClassificationType[] baseTypes) { + return new MockClassificationType(String.Empty, baseTypes); + } + + public IClassificationType CreateTransientClassificationType(IEnumerable baseTypes) { + return new MockClassificationType(String.Empty, baseTypes.ToArray()); + } + + public IClassificationType GetClassificationType(string type) { + MockClassificationType result; + return _types.TryGetValue(type, out result) ? result : null; + } + } +} diff --git a/Tools/IronStudio/UnitTests/MockComponentModel.cs b/Tools/IronStudio/UnitTests/MockComponentModel.cs new file mode 100644 index 0000000000..4d6622d24e --- /dev/null +++ b/Tools/IronStudio/UnitTests/MockComponentModel.cs @@ -0,0 +1,56 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.ComponentModelHost; +using Microsoft.VisualStudio.Text.Adornments; +using Microsoft.IronStudio.Core; +using Microsoft.IronPythonTools; + +namespace UnitTests { + class MockComponentModel : IComponentModel { + + public T GetService() where T : class { + if (typeof(T) == typeof(IPythonRuntimeHost)) { + return (T)(object)new MockDlrRuntimeHost(); + } else if (typeof(T) == typeof(IErrorProviderFactory)) { + return (T)(object)new MockErrorProviderFactory(); + } + throw new InvalidOperationException(); + } + + public System.ComponentModel.Composition.Primitives.ComposablePartCatalog DefaultCatalog { + get { throw new NotImplementedException(); } + } + + public System.ComponentModel.Composition.ICompositionService DefaultCompositionService { + get { throw new NotImplementedException(); } + } + + public System.ComponentModel.Composition.Hosting.ExportProvider DefaultExportProvider { + get { throw new NotImplementedException(); } + } + + public System.ComponentModel.Composition.Primitives.ComposablePartCatalog GetCatalog(string catalogName) { + throw new NotImplementedException(); + } + + public IEnumerable GetExtensions() where T : class { + throw new NotImplementedException(); + } + } +} diff --git a/Tools/IronStudio/UnitTests/MockContentType.cs b/Tools/IronStudio/UnitTests/MockContentType.cs new file mode 100644 index 0000000000..a9b9a5133c --- /dev/null +++ b/Tools/IronStudio/UnitTests/MockContentType.cs @@ -0,0 +1,58 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Text.Classification; +using Microsoft.VisualStudio.Utilities; + +namespace UnitTests { + class MockContentType : IContentType { + private readonly string _name; + private readonly IContentType[] _bases; + + public MockContentType(string name, IContentType[] bases) { + _name = name; + _bases = bases; + } + + public IEnumerable BaseTypes { + get { return _bases; } + } + + public bool IsOfType(string type) { + if (type == _name) { + return true; + } + + foreach (var baseType in BaseTypes) { + if (baseType.IsOfType(type)) { + return true; + } + } + return false; + } + + + public string DisplayName { + get { return _name; } + } + + public string TypeName { + get { return _name; } + } + } +} diff --git a/Tools/IronStudio/UnitTests/MockDlrRuntimeHost.cs b/Tools/IronStudio/UnitTests/MockDlrRuntimeHost.cs new file mode 100644 index 0000000000..6aad4427ac --- /dev/null +++ b/Tools/IronStudio/UnitTests/MockDlrRuntimeHost.cs @@ -0,0 +1,47 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.IronStudio.Core; +using Microsoft.IronPythonTools; + +namespace UnitTests { + class MockDlrRuntimeHost : IPythonRuntimeHost { + public Microsoft.Scripting.Hosting.ScriptEngine ScriptEngine { + get { return Program.PythonEngine; } + } + + public Microsoft.VisualStudio.Utilities.IContentType ContentType { + get { return Program.PythonContentType; } + } + + public bool EnterOutliningModeOnOpen { + get { return false; } + set { } + } + + public bool IntersectMembers { + get { return true; } + set { } + } + + public bool HideAdvancedMembers { + get { return false; } + set { } + } + } +} diff --git a/Tools/IronStudio/UnitTests/MockErrorProviderFactory.cs b/Tools/IronStudio/UnitTests/MockErrorProviderFactory.cs new file mode 100644 index 0000000000..e62ac758dc --- /dev/null +++ b/Tools/IronStudio/UnitTests/MockErrorProviderFactory.cs @@ -0,0 +1,28 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Text.Adornments; +using Microsoft.VisualStudio.Text.Tagging; + +namespace UnitTests { + class MockErrorProviderFactory : IErrorProviderFactory { + public SimpleTagger GetErrorTagger(Microsoft.VisualStudio.Text.ITextBuffer textBuffer) { + return new SimpleTagger(textBuffer); + } + } +} diff --git a/Tools/IronStudio/UnitTests/MockTextBuffer.cs b/Tools/IronStudio/UnitTests/MockTextBuffer.cs new file mode 100644 index 0000000000..240a1109bf --- /dev/null +++ b/Tools/IronStudio/UnitTests/MockTextBuffer.cs @@ -0,0 +1,140 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Text; +using Microsoft.VisualStudio.Utilities; +using Microsoft.IronStudio; +using Microsoft.IronStudio.Library; +using Microsoft.IronPythonTools; + +namespace UnitTests { + class MockTextBuffer : ITextBuffer { + internal readonly string _text; + private PropertyCollection _properties; + + public MockTextBuffer(string content) { + _text = content; + } + + public void ChangeContentType(Microsoft.VisualStudio.Utilities.IContentType newContentType, object editTag) { + throw new NotImplementedException(); + } +#pragma warning disable 67 + public event EventHandler Changed; + + public event EventHandler ChangedHighPriority; + + public event EventHandler ChangedLowPriority; + + public event EventHandler Changing; + + public event EventHandler PostChanged; + + public event EventHandler ReadOnlyRegionsChanged; + + public event EventHandler ContentTypeChanged; + +#pragma warning restore 67 + + public bool CheckEditAccess() { + throw new NotImplementedException(); + } + + public Microsoft.VisualStudio.Utilities.IContentType ContentType { + get { return Program.PythonContentType; } + } + + public ITextEdit CreateEdit() { + throw new NotImplementedException(); + } + + public ITextEdit CreateEdit(EditOptions options, int? reiteratedVersionNumber, object editTag) { + throw new NotImplementedException(); + } + + public IReadOnlyRegionEdit CreateReadOnlyRegionEdit() { + throw new NotImplementedException(); + } + + public ITextSnapshot CurrentSnapshot { + get { return new MockTextSnapshot(this); } + } + + public ITextSnapshot Delete(Span deleteSpan) { + throw new NotImplementedException(); + } + + public bool EditInProgress { + get { throw new NotImplementedException(); } + } + + public NormalizedSpanCollection GetReadOnlyExtents(Span span) { + throw new NotImplementedException(); + } + + public ITextSnapshot Insert(int position, string text) { + throw new NotImplementedException(); + } + + public bool IsReadOnly(Span span, bool isEdit) { + throw new NotImplementedException(); + } + + public bool IsReadOnly(Span span) { + throw new NotImplementedException(); + } + + public bool IsReadOnly(int position, bool isEdit) { + throw new NotImplementedException(); + } + + public bool IsReadOnly(int position) { + throw new NotImplementedException(); + } + + public ITextSnapshot Replace(Span replaceSpan, string replaceWith) { + throw new NotImplementedException(); + } + + public void TakeThreadOwnership() { + throw new NotImplementedException(); + } + + private static readonly IDlrClassifierProvider _classProvider = MakeClassifierProvider(); + + private static IDlrClassifierProvider MakeClassifierProvider() { + var classReg = new MockClassificationTypeRegistryService(); + + var provider = new PythonClassifierProvider(Program.PythonContentType, Program.PythonEngine); + provider._classificationRegistry = classReg; + return provider; + } + + public Microsoft.VisualStudio.Utilities.PropertyCollection Properties { + get { + if (_properties == null) { + _properties = new PropertyCollection(); + + _classProvider.GetClassifier(this); + } + + return _properties; + } + } + } +} diff --git a/Tools/IronStudio/UnitTests/MockTextSnapshot.cs b/Tools/IronStudio/UnitTests/MockTextSnapshot.cs new file mode 100644 index 0000000000..60c2193d76 --- /dev/null +++ b/Tools/IronStudio/UnitTests/MockTextSnapshot.cs @@ -0,0 +1,139 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Text; + +namespace UnitTests { + class MockTextSnapshot : ITextSnapshot { + private readonly MockTextBuffer _buffer; + + public MockTextSnapshot(MockTextBuffer mockTextBuffer) { + _buffer = mockTextBuffer; + } + + public Microsoft.VisualStudio.Utilities.IContentType ContentType { + get { throw new NotImplementedException(); } + } + + public void CopyTo(int sourceIndex, char[] destination, int destinationIndex, int count) { + _buffer._text.CopyTo(sourceIndex, destination, destinationIndex, count); + } + + public ITrackingPoint CreateTrackingPoint(int position, PointTrackingMode trackingMode, TrackingFidelityMode trackingFidelity) { + throw new NotImplementedException(); + } + + public ITrackingPoint CreateTrackingPoint(int position, PointTrackingMode trackingMode) { + throw new NotImplementedException(); + } + + public ITrackingSpan CreateTrackingSpan(int start, int length, SpanTrackingMode trackingMode, TrackingFidelityMode trackingFidelity) { + throw new NotImplementedException(); + } + + public ITrackingSpan CreateTrackingSpan(int start, int length, SpanTrackingMode trackingMode) { + throw new NotImplementedException(); + } + + public ITrackingSpan CreateTrackingSpan(Span span, SpanTrackingMode trackingMode, TrackingFidelityMode trackingFidelity) { + throw new NotImplementedException(); + } + + public ITrackingSpan CreateTrackingSpan(Span span, SpanTrackingMode trackingMode) { + return new MockTrackingSpan(this, span.Start, span.Length); + } + + private string[] GetLines() { + return _buffer._text.Split(new[] { "\r\n" }, StringSplitOptions.None); + } + + public ITextSnapshotLine GetLineFromLineNumber(int lineNumber) { + string[] lines = GetLines(); + for (int i = 0, curPosition = 0; i < lines.Length; i++) { + if (i == lineNumber) { + return new MockTextSnapshotLine(this, lines[i], i, curPosition); + } + curPosition += lines[i].Length + 2; + } + return new MockTextSnapshotLine(this, "", lines.Length, _buffer._text.Length); + } + + public ITextSnapshotLine GetLineFromPosition(int position) { + string[] lines = GetLines(); + for(int i = 0, curPosition = 0; i= position) { + return new MockTextSnapshotLine(this, lines[i], i, curPosition); + } + curPosition += lines[i].Length + 2; + } + return new MockTextSnapshotLine(this, "", lines.Length, _buffer._text.Length); + } + + public int GetLineNumberFromPosition(int position) { + return GetLineFromPosition(position).LineNumber; + } + + public string GetText() { + return _buffer._text; + } + + public string GetText(int startIndex, int length) { + return GetText().Substring(startIndex, length); + } + + public string GetText(Span span) { + return GetText().Substring(span.Start, span.Length); + } + + public int Length { + get { return _buffer._text.Length; } + } + + public int LineCount { + get { return GetLines().Length; } + } + + public IEnumerable Lines { + get { throw new NotImplementedException(); } + } + + public ITextBuffer TextBuffer { + get { return _buffer; } + } + + public char[] ToCharArray(int startIndex, int length) { + throw new NotImplementedException(); + } + + public ITextVersion Version { + get { return new MockTextVersion(); } + } + + public void Write(System.IO.TextWriter writer) { + throw new NotImplementedException(); + } + + public void Write(System.IO.TextWriter writer, Span span) { + throw new NotImplementedException(); + } + + public char this[int position] { + get { return _buffer._text[position]; } + } + } +} diff --git a/Tools/IronStudio/UnitTests/MockTextSnapshotLine.cs b/Tools/IronStudio/UnitTests/MockTextSnapshotLine.cs new file mode 100644 index 0000000000..5e7e732a48 --- /dev/null +++ b/Tools/IronStudio/UnitTests/MockTextSnapshotLine.cs @@ -0,0 +1,88 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Text; +using System.Diagnostics; + +namespace UnitTests { + class MockTextSnapshotLine : ITextSnapshotLine { + private readonly MockTextSnapshot _snapshot; + private readonly string _text; + private readonly int _lineNo, _startPos; + + public MockTextSnapshotLine(MockTextSnapshot snapshot, string text, int lineNo, int startPos) { + Debug.Assert(!text.EndsWith("\n")); + _snapshot = snapshot; + _text = text; + _lineNo = lineNo; + _startPos = startPos; + } + + public SnapshotPoint End { + get { return new SnapshotPoint(_snapshot, _startPos + _text.Length); } + } + + public SnapshotPoint EndIncludingLineBreak { + get { throw new NotImplementedException(); } + } + + public SnapshotSpan Extent { + get { return new SnapshotSpan(Start, End); } + } + + public SnapshotSpan ExtentIncludingLineBreak { + get { throw new NotImplementedException(); } + } + + public string GetLineBreakText() { + return "\r\n"; + } + + public string GetText() { + return _text; + } + + public string GetTextIncludingLineBreak() { + return _text + GetLineBreakText(); + } + + public int Length { + get { return _text.Length; } + } + + public int LengthIncludingLineBreak { + get { return _text.Length + LineBreakLength; } + } + + public int LineBreakLength { + get { return 2; } + } + + public int LineNumber { + get { return _lineNo; } + } + + public ITextSnapshot Snapshot { + get { return _snapshot; } + } + + public SnapshotPoint Start { + get { return new SnapshotPoint(_snapshot, _startPos); } + } + } +} diff --git a/Tools/IronStudio/UnitTests/MockTextVersion.cs b/Tools/IronStudio/UnitTests/MockTextVersion.cs new file mode 100644 index 0000000000..98a09bae89 --- /dev/null +++ b/Tools/IronStudio/UnitTests/MockTextVersion.cs @@ -0,0 +1,75 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Text; + +namespace UnitTests { + class MockTextVersion : ITextVersion { + public INormalizedTextChangeCollection Changes { + get { throw new NotImplementedException(); } + } + + public ITrackingSpan CreateCustomTrackingSpan(Span span, TrackingFidelityMode trackingFidelity, object customState, CustomTrackToVersion behavior) { + throw new NotImplementedException(); + } + + public ITrackingPoint CreateTrackingPoint(int position, PointTrackingMode trackingMode, TrackingFidelityMode trackingFidelity) { + throw new NotImplementedException(); + } + + public ITrackingPoint CreateTrackingPoint(int position, PointTrackingMode trackingMode) { + throw new NotImplementedException(); + } + + public ITrackingSpan CreateTrackingSpan(int start, int length, SpanTrackingMode trackingMode, TrackingFidelityMode trackingFidelity) { + throw new NotImplementedException(); + } + + public ITrackingSpan CreateTrackingSpan(int start, int length, SpanTrackingMode trackingMode) { + throw new NotImplementedException(); + } + + public ITrackingSpan CreateTrackingSpan(Span span, SpanTrackingMode trackingMode, TrackingFidelityMode trackingFidelity) { + throw new NotImplementedException(); + } + + public ITrackingSpan CreateTrackingSpan(Span span, SpanTrackingMode trackingMode) { + throw new NotImplementedException(); + } + + public int Length { + get { throw new NotImplementedException(); } + } + + public ITextVersion Next { + get { throw new NotImplementedException(); } + } + + public int ReiteratedVersionNumber { + get { throw new NotImplementedException(); } + } + + public ITextBuffer TextBuffer { + get { throw new NotImplementedException(); } + } + + public int VersionNumber { + get { throw new NotImplementedException(); } + } + } +} diff --git a/Tools/IronStudio/UnitTests/MockTextView.cs b/Tools/IronStudio/UnitTests/MockTextView.cs new file mode 100644 index 0000000000..e8994e0d6a --- /dev/null +++ b/Tools/IronStudio/UnitTests/MockTextView.cs @@ -0,0 +1,184 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Text.Editor; +using Microsoft.VisualStudio.Text; + +namespace UnitTests { + class MockTextView : ITextView { + private readonly ITextBuffer _buffer; + + public MockTextView(ITextBuffer buffer) { + _buffer = buffer; + } + + public Microsoft.VisualStudio.Text.Projection.IBufferGraph BufferGraph { + get { throw new NotImplementedException(); } + } + + public ITextCaret Caret { + get { throw new NotImplementedException(); } + } + + public void Close() { + throw new NotImplementedException(); + } + + public event EventHandler Closed; + + public void DisplayTextLineContainingBufferPosition(Microsoft.VisualStudio.Text.SnapshotPoint bufferPosition, double verticalDistance, ViewRelativePosition relativeTo, double? viewportWidthOverride, double? viewportHeightOverride) { + throw new NotImplementedException(); + } + + public void DisplayTextLineContainingBufferPosition(Microsoft.VisualStudio.Text.SnapshotPoint bufferPosition, double verticalDistance, ViewRelativePosition relativeTo) { + throw new NotImplementedException(); + } + + public Microsoft.VisualStudio.Text.SnapshotSpan GetTextElementSpan(Microsoft.VisualStudio.Text.SnapshotPoint point) { + throw new NotImplementedException(); + } + + public Microsoft.VisualStudio.Text.Formatting.ITextViewLine GetTextViewLineContainingBufferPosition(Microsoft.VisualStudio.Text.SnapshotPoint bufferPosition) { + throw new NotImplementedException(); + } + + public event EventHandler GotAggregateFocus; + + public bool HasAggregateFocus { + get { throw new NotImplementedException(); } + } + + public bool InLayout { + get { throw new NotImplementedException(); } + } + + public bool IsClosed { + get { throw new NotImplementedException(); } + } + + public bool IsMouseOverViewOrAdornments { + get { throw new NotImplementedException(); } + } + + public event EventHandler LayoutChanged; + + public double LineHeight { + get { throw new NotImplementedException(); } + } + + public event EventHandler LostAggregateFocus; + + public double MaxTextRightCoordinate { + get { throw new NotImplementedException(); } + } + + public event EventHandler MouseHover; + + public IEditorOptions Options { + get { throw new NotImplementedException(); } + } + + public Microsoft.VisualStudio.Text.ITrackingSpan ProvisionalTextHighlight { + get { + throw new NotImplementedException(); + } + set { + throw new NotImplementedException(); + } + } + + public void QueueSpaceReservationStackRefresh() { + throw new NotImplementedException(); + } + + public ITextViewRoleSet Roles { + get { throw new NotImplementedException(); } + } + + public ITextSelection Selection { + get { throw new NotImplementedException(); } + } + + public ITextBuffer TextBuffer { + get { return _buffer; } + } + + public Microsoft.VisualStudio.Text.ITextDataModel TextDataModel { + get { throw new NotImplementedException(); } + } + + public Microsoft.VisualStudio.Text.ITextSnapshot TextSnapshot { + get { throw new NotImplementedException(); } + } + + public ITextViewLineCollection TextViewLines { + get { throw new NotImplementedException(); } + } + + public ITextViewModel TextViewModel { + get { throw new NotImplementedException(); } + } + + public IViewScroller ViewScroller { + get { throw new NotImplementedException(); } + } + + public double ViewportBottom { + get { throw new NotImplementedException(); } + } + + public double ViewportHeight { + get { throw new NotImplementedException(); } + } + + public event EventHandler ViewportHeightChanged; + + public double ViewportLeft { + get { + throw new NotImplementedException(); + } + set { + throw new NotImplementedException(); + } + } + + public event EventHandler ViewportLeftChanged; + + public double ViewportRight { + get { throw new NotImplementedException(); } + } + + public double ViewportTop { + get { throw new NotImplementedException(); } + } + + public double ViewportWidth { + get { throw new NotImplementedException(); } + } + + public event EventHandler ViewportWidthChanged; + + public Microsoft.VisualStudio.Text.ITextSnapshot VisualSnapshot { + get { throw new NotImplementedException(); } + } + + public Microsoft.VisualStudio.Utilities.PropertyCollection Properties { + get { throw new NotImplementedException(); } + } + } +} diff --git a/Tools/IronStudio/UnitTests/MockTrackingSpan.cs b/Tools/IronStudio/UnitTests/MockTrackingSpan.cs new file mode 100644 index 0000000000..f0fff59307 --- /dev/null +++ b/Tools/IronStudio/UnitTests/MockTrackingSpan.cs @@ -0,0 +1,64 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using System.Collections.Generic; +using System.Linq; +using System.Text; +using Microsoft.VisualStudio.Text; + +namespace UnitTests { + class MockTrackingSpan : ITrackingSpan { + private readonly int _start, _length; + private readonly MockTextSnapshot _snapshot; + + public MockTrackingSpan(MockTextSnapshot snapshot, int start, int length) { + _start = start; + _length = length; + _snapshot = snapshot; + } + + public SnapshotPoint GetEndPoint(ITextSnapshot snapshot) { + return new SnapshotPoint(_snapshot, _start + _length); + } + + public Span GetSpan(ITextVersion version) { + return new Span(_start, _length); + } + + public SnapshotSpan GetSpan(ITextSnapshot snapshot) { + return new SnapshotSpan(snapshot, new Span(_start, _length)); + } + + public SnapshotPoint GetStartPoint(ITextSnapshot snapshot) { + throw new NotImplementedException(); + } + + public string GetText(ITextSnapshot snapshot) { + throw new NotImplementedException(); + } + + public ITextBuffer TextBuffer { + get { throw new NotImplementedException(); } + } + + public TrackingFidelityMode TrackingFidelity { + get { throw new NotImplementedException(); } + } + + public SpanTrackingMode TrackingMode { + get { throw new NotImplementedException(); } + } + } +} diff --git a/Tools/IronStudio/UnitTests/Program.cs b/Tools/IronStudio/UnitTests/Program.cs new file mode 100644 index 0000000000..c919d6196f --- /dev/null +++ b/Tools/IronStudio/UnitTests/Program.cs @@ -0,0 +1,147 @@ +/* **************************************************************************** + * + * Copyright (c) Microsoft Corporation. + * + * This source code is subject to terms and conditions of the Apache License, Version 2.0. A + * copy of the license can be found in the License.html file at the root of this distribution. If + * you cannot locate the Apache License, Version 2.0, please send an email to + * ironpy@microsoft.com. By using this source code in any fashion, you are agreeing to be bound + * by the terms of the Apache License, Version 2.0. + * + * You must not remove this notice, or any other, from this software. + * + * ***************************************************************************/ + +using System; +using Microsoft.IronStudio; +using IronPython.Hosting; +using System.Collections.Generic; +using System.IO; +using System.Text; +using System.Reflection; +using System.Threading; +using Microsoft.Scripting.Hosting; +using System.Linq; +using Microsoft.VisualStudio.TestTools.UnitTesting; +using Microsoft.IronPythonTools.Library.Repl; +using Microsoft.IronStudio.RemoteEvaluation; + +namespace UnitTests { + [TestClass] + public partial class Program { + public static void Main(string[] args) { + var inst = new Program(); + foreach (var method in typeof(Program).GetMethods()) { + if (method.Name.StartsWith("Scenario_")) { + if (args.Length > 0 && !args.Contains(method.Name)) { + Console.Write("Skipping Scenario {0} ", method.Name); + continue; + } + Console.Write("Scenario {0} ", method.Name); + try { + method.Invoke(inst, new object[0]); + Console.WriteLine("PASSED"); + } catch (Exception e) { + Console.WriteLine(e.ToString()); + Console.WriteLine("FAILED"); + } + } + } + } + + [TestMethod] + public void Scenario_RemoteScriptFactory() { + using (var factory = RemotePythonEvaluator.CreateFactory()) { + var runtime = (ScriptRuntime)factory.CreateRuntime(Python.CreateRuntimeSetup(new Dictionary())); + StringWriter writer = new StringWriter(); + StringWriter errWriter = new StringWriter(); + + runtime.IO.SetOutput(Stream.Null, writer); + factory.SetConsoleOut(writer); + factory.SetConsoleError(errWriter); + + // verify print goes to the correct output + var engine = runtime.GetEngine("Python"); + engine.Execute("print 'hello'"); + var builder = writer.GetStringBuilder(); + + AreEqual(builder.ToString(), "hello\r\n"); + builder.Clear(); + + // verify Console.WriteLine is redirected + engine.Execute("import System\nSystem.Console.WriteLine('hello')\n"); + AreEqual(builder.ToString(), "hello\r\n"); + builder.Clear(); + + // verify Console.Error.WriteLine is redirected to stderr + var errBuilder = errWriter.GetStringBuilder(); + engine.Execute("import System\nSystem.Console.Error.WriteLine('hello')\n"); + AreEqual(errBuilder.ToString(), "hello\r\n"); + errBuilder.Clear(); + + // raise an exception, should be propagated back + try { + engine.Execute("import System\nraise System.ArgumentException()\n"); + AreEqual(true, false); + } catch (ArgumentException) { + } + + /* + // verify that all code runs on the same thread + var scope = engine.CreateScope(); + engine.Execute("import System"); + + + List res = new List(); + for (int i = 0; i < 100; i++) { + ThreadPool.QueueUserWorkItem( + (x) => { + object value = engine.Execute("System.Threading.Thread.CurrentThread.ManagedThreadId", scope); + lock (res) { + res.Add(value); + } + }); + } + + while (res.Count != 100) { + Thread.Sleep(100); + } + + for (int i = 1; i < res.Count; i++) { + if (!res[i - 1].Equals(res[i])) { + throw new Exception("running on multiple threads"); + } + }*/ + + // create a long running work item, execute it, and then make sure we can continue to execute work items. + ThreadPool.QueueUserWorkItem(x => { + engine.Execute("while True: pass"); + }); + Thread.Sleep(1000); + factory.Abort(); + + AreEqual(engine.Execute("42"), 42); + } + + // check starting on an MTA thread + using (var factory = new RemoteScriptFactory(ApartmentState.MTA)) { + var runtime = (ScriptRuntime)factory.CreateRuntime(Python.CreateRuntimeSetup(new Dictionary())); + var engine = runtime.GetEngine("Python"); + AreEqual(engine.Execute("import System\nSystem.Threading.Thread.CurrentThread.ApartmentState == System.Threading.ApartmentState.MTA"), true); + } + + // check starting on an STA thread + using (var factory = new RemoteScriptFactory(ApartmentState.STA)) { + var runtime = (ScriptRuntime)factory.CreateRuntime(Python.CreateRuntimeSetup(new Dictionary())); + var engine = runtime.GetEngine("Python"); + AreEqual(engine.Execute("import System\nSystem.Threading.Thread.CurrentThread.ApartmentState == System.Threading.ApartmentState.STA"), true); + } + } + + private static void AreEqual(object x, object y) { + if (!x.Equals(y)) { + throw new Exception(String.Format("Expected {0}, got {1}", y, x)); + } + } + } +} diff --git a/Tools/IronStudio/UnitTests/Properties/AssemblyInfo.cs b/Tools/IronStudio/UnitTests/Properties/AssemblyInfo.cs new file mode 100644 index 0000000000..7d78320b60 --- /dev/null +++ b/Tools/IronStudio/UnitTests/Properties/AssemblyInfo.cs @@ -0,0 +1,36 @@ +using System.Reflection; +using System.Runtime.CompilerServices; +using System.Runtime.InteropServices; + +// General Information about an assembly is controlled through the following +// set of attributes. Change these attribute values to modify the information +// associated with an assembly. +[assembly: AssemblyTitle("UnitTests")] +[assembly: AssemblyDescription("")] +[assembly: AssemblyConfiguration("")] +[assembly: AssemblyCompany("Microsoft IT")] +[assembly: AssemblyProduct("UnitTests")] +[assembly: AssemblyCopyright("Copyright © Microsoft IT 2010")] +[assembly: AssemblyTrademark("")] +[assembly: AssemblyCulture("")] + +// Setting ComVisible to false makes the types in this assembly not visible +// to COM components. If you need to access a type in this assembly from +// COM, set the ComVisible attribute to true on that type. +[assembly: ComVisible(false)] + +// The following GUID is for the ID of the typelib if this project is exposed to COM +[assembly: Guid("7e8c28ef-e2e6-40ad-97cb-cd6af2fbe37c")] + +// Version information for an assembly consists of the following four values: +// +// Major Version +// Minor Version +// Build Number +// Revision +// +// You can specify all the values or you can default the Build and Revision Numbers +// by using the '*' as shown below: +// [assembly: AssemblyVersion("1.0.*")] +[assembly: AssemblyVersion("1.0.0.0")] +[assembly: AssemblyFileVersion("1.0.0.0")] diff --git a/Tools/IronStudio/UnitTests/UnitTests.csproj b/Tools/IronStudio/UnitTests/UnitTests.csproj new file mode 100644 index 0000000000..a3c08f2779 --- /dev/null +++ b/Tools/IronStudio/UnitTests/UnitTests.csproj @@ -0,0 +1,132 @@ + + + + + Debug + x86 + 8.0.30703 + 2.0 + {4D7002C8-C760-4568-BD31-DAB65F88131A} + Exe + Properties + UnitTests + UnitTests + v4.0 + + + 512 + SAK + SAK + SAK + SAK + {3AC096D0-A1C2-E12C-1390-A8335801FDAB};{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC} + + + x86 + true + full + false + $(SolutionDir)..\bin\Debug + DEBUG;TRACE + prompt + 4 + + + AnyCPU + pdbonly + true + $(SolutionDir)..\bin\Release + TRACE + prompt + 4 + + + + + + ..\..\..\Util\Internal\Maui\Microsoft.VisualStudio.Internal.PropertyManager.dll + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + {155CE436-1669-4A48-8095-410F2430237F} + IronPython.Modules + + + {95289EA9-5778-489D-AB48-F81F2CE2DA32} + IronPython %28Languages\IronPython\IronPython%29 + + + {EB66B766-6354-4208-A3D4-AACBDCB5C3B3} + Microsoft.Dynamic + + + {02FF0909-F5AD-48CF-A86A-345E721B7E40} + Microsoft.Scripting + + + {B2E267A2-4836-40D7-817E-49D9D0E88435} + IronPythonToolsCore + + + {FA7BE5F5-E04F-4613-B7AC-70CE10D1BB68} + IronPythonTools + + + {843716AE-38B3-4723-963C-950DD06BC4B8} + IronStudioCore + + + {C98DE16D-417E-4079-8939-1F8C8E28E2C8} + IronStudio + + + {D10C905C-7F15-41DF-9FF9-CCE461F571FD} + RemoteScriptFactory + + + + + + + + + + + \ No newline at end of file diff --git a/Tools/IronStudio/UnitTests/UnitTests.csproj.vspscc b/Tools/IronStudio/UnitTests/UnitTests.csproj.vspscc new file mode 100644 index 0000000000..feffdecaa4 --- /dev/null +++ b/Tools/IronStudio/UnitTests/UnitTests.csproj.vspscc @@ -0,0 +1,10 @@ +"" +{ +"FILE_VERSION" = "9237" +"ENLISTMENT_CHOICE" = "NEVER" +"PROJECT_FILE_RELATIVE_PATH" = "" +"NUMBER_OF_EXCLUDED_FILES" = "0" +"ORIGINAL_PROJECT_FILE_PATH" = "" +"NUMBER_OF_NESTED_PROJECTS" = "0" +"SOURCE_CONTROL_SETTINGS_PROVIDER" = "PROVIDER" +} diff --git a/Tools/IronStudio/UnitTests/app.config b/Tools/IronStudio/UnitTests/app.config new file mode 100644 index 0000000000..cb2586beb1 --- /dev/null +++ b/Tools/IronStudio/UnitTests/app.config @@ -0,0 +1,3 @@ + + +