Example #1
0
        public static bool CanNavigate(MSBuildRootDocument doc, DocumentLocation location, MSBuildResolveResult rr)
        {
            if (rr == null)
            {
                return(false);
            }

            var annotations = GetAnnotationsAtLocation <NavigationAnnotation> (doc, location);

            if (annotations != null && annotations.Any())
            {
                return(true);
            }

            if (rr.ReferenceKind == MSBuildReferenceKind.Target)
            {
                return(true);
            }

            if (rr.ReferenceKind == MSBuildReferenceKind.FileOrFolder)
            {
                return(true);
            }

            return(false);
        }
        public static List <MSBuildNavigationResult> ResolveAll(MSBuildRootDocument doc)
        {
            var visitor = new MSBuildNavigationVisitor();

            visitor.Run(doc);
            return(visitor.Navigations);
        }
Example #3
0
        public static List <MSBuildNavigationResult> ResolveAll(MSBuildRootDocument doc, int offset, int length)
        {
            var visitor = new MSBuildNavigationVisitor();

            visitor.Run(doc, offset, length);
            return(visitor.Navigations);
        }
Example #4
0
        public static IEnumerable <BaseInfo> GetCompletionInfos(
            MSBuildResolveResult rr,
            TriggerState trigger, MSBuildValueKind kind,
            ExpressionNode triggerExpression, int triggerLength,
            MSBuildRootDocument doc)
        {
            switch (trigger)
            {
            case TriggerState.Value:
                return(MSBuildCompletionExtensions.GetValueCompletions(kind, doc, rr));

            case TriggerState.Item:
                return(doc.GetItems());

            case TriggerState.Metadata:
                return(doc.GetMetadata(null, true));

            case TriggerState.Property:
                return(doc.GetProperties(true));

            case TriggerState.MetadataOrItem:
                return(((IEnumerable <BaseInfo>)doc.GetItems()).Concat(doc.GetMetadata(null, true)));

            case TriggerState.DirectorySeparator:
                return(MSBuildCompletionExtensions.GetFilenameCompletions(kind, doc, triggerExpression, triggerLength));;

            case TriggerState.MethodName:
                return(FunctionCompletion.GetMethodNameCompletions(triggerExpression));
            }
            throw new InvalidOperationException();
        }
Example #5
0
        public static MSBuildNavigationResult GetNavigation(
            MSBuildRootDocument doc, DocumentLocation location, MSBuildResolveResult rr)
        {
            if (rr == null)
            {
                return(null);
            }

            //HACK: we should really use the ITextSource directly, but since the XML parser positions are
            //currently line/col, we need a TextDocument to convert to offsets
            var textDocument = doc.Text as IReadonlyTextDocument
                               ?? TextEditorFactory.CreateNewReadonlyDocument(
                doc.Text, doc.Filename, MSBuildTextEditorExtension.MSBuildMimeType
                );

            var annotations     = GetAnnotationsAtLocation <NavigationAnnotation> (doc, location);
            var firstAnnotation = annotations.FirstOrDefault();

            if (firstAnnotation != null)
            {
                var beginOffset = textDocument.LocationToOffset(firstAnnotation.Region.Begin);
                var endOffset   = textDocument.LocationToOffset(firstAnnotation.Region.End);
                return(new MSBuildNavigationResult(
                           annotations.Select(a => a.Path).ToArray(), beginOffset, endOffset - beginOffset + 1
                           ));
            }

            if (rr.ReferenceKind == MSBuildReferenceKind.Target)
            {
                return(new MSBuildNavigationResult(
                           MSBuildReferenceKind.Target, (string)rr.Reference, rr.ReferenceOffset, rr.ReferenceLength
                           ));
            }

            if (rr.ReferenceKind == MSBuildReferenceKind.FileOrFolder)
            {
                return(new MSBuildNavigationResult(
                           (string[])rr.Reference, rr.ReferenceOffset, rr.ReferenceLength
                           ));
            }

            if (rr.ReferenceKind == MSBuildReferenceKind.Task)
            {
                var task = doc.GetTask((string)rr.Reference);
                if (task.DeclaredInFile != null)
                {
                    return(new MSBuildNavigationResult(
                               MSBuildReferenceKind.Task, (string)rr.Reference, rr.ReferenceOffset, rr.ReferenceLength,
                               task.DeclaredInFile, task.DeclaredAtLocation
                               ));
                }
            }

            return(null);
        }
Example #6
0
        public static IEnumerable <T> GetAnnotationsAtLocation <T> (MSBuildRootDocument doc, DocumentLocation location)
        {
            var xobj = doc.XDocument.FindNodeAtLocation(location);

            if (xobj == null)
            {
                return(null);
            }
            return(doc.Annotations
                   .GetMany <T> (xobj)
                   .Where(a => !(a is IRegionAnnotation ra) || ra.Region.Contains(location)));
        }
Example #7
0
        public static IEnumerable <BaseInfo> GetComparandCompletions(MSBuildRootDocument doc, IReadOnlyList <ExpressionNode> variables)
        {
            var names = new HashSet <string> ();

            foreach (var variable in variables)
            {
                ValueInfo info;
                switch (variable)
                {
                case ExpressionProperty ep:
                    if (ep.IsSimpleProperty)
                    {
                        info = doc.GetProperty(ep.Name) ?? new PropertyInfo(ep.Name, null, false);
                        break;
                    }
                    continue;

                case ExpressionMetadata em:
                    info = doc.GetMetadata(em.ItemName, em.MetadataName, true) ?? new MetadataInfo(em.MetadataName, null, false);
                    break;

                default:
                    continue;
                }

                if (info == null)
                {
                    continue;
                }

                IEnumerable <BaseInfo> cinfos;
                if (info.Values != null && info.Values.Count > 0)
                {
                    cinfos = info.Values;
                }
                else
                {
                    var kind = info.InferValueKindIfUnknown();
                    cinfos = MSBuildCompletionExtensions.GetValueCompletions(kind, doc);
                }

                if (cinfos != null)
                {
                    foreach (var ci in cinfos)
                    {
                        if (names.Add(ci.Name))
                        {
                            yield return(ci);
                        }
                    }
                }
            }
        }
Example #8
0
 void LoadTasks(
     HashSet <string> importedFiles, MSBuildRootDocument previous, string label, string filename,
     PropertyValueCollector propVals, TaskMetadataBuilder taskBuilder, MSBuildSchemaProvider schemaProvider,
     CancellationToken token)
 {
     try {
         var import = GetCachedOrParse(importedFiles, previous, label, filename, null, File.GetLastWriteTimeUtc(filename), Filename, propVals, taskBuilder, schemaProvider, token);
         AddImport(import);
     } catch (Exception ex) {
         LoggingService.LogError($"Error loading tasks file {filename}", ex);
     }
 }
Example #9
0
 public static MSBuildNavigationResult GetPathFromNode(ExpressionNode node, MSBuildRootDocument document)
 {
     try {
         var path = MSBuildCompletionExtensions.EvaluateExpressionAsPaths(node, document).FirstOrDefault();
         if (path != null && File.Exists(path))
         {
             return(new MSBuildNavigationResult(
                        new [] { path }, node.Offset, node.Length
                        ));
         }
     } catch (Exception ex) {
         Core.LoggingService.LogError($"Error checking path for file '{node}'", ex);
     }
     return(null);
 }
Example #10
0
 Import GetCachedOrParse(
     HashSet <string> importedFiles, MSBuildRootDocument oldDoc, string importExpr, string resolvedFilename, string sdk, DateTime mtimeUtc, string projectPath,
     PropertyValueCollector propVals, TaskMetadataBuilder taskBuilder, MSBuildSchemaProvider schemaProvider,
     CancellationToken token)
 {
     if (oldDoc != null && oldDoc.resolvedImportsMap.TryGetValue(resolvedFilename ?? importExpr, out Import oldImport) && oldImport.TimeStampUtc == mtimeUtc)
     {
         //TODO: check mtimes of descendent imports too
         return(oldImport);
     }
     else
     {
         //TODO: guard against cyclic imports
         return(ParseImport(importedFiles, new Import(importExpr, sdk, resolvedFilename, mtimeUtc), projectPath, propVals, taskBuilder, schemaProvider, token));
     }
 }
 public void Run(MSBuildRootDocument doc, int offset = 0, int length = 0)
 {
     Run(doc.XDocument, doc.Filename, doc.Text, doc, offset, length);
 }
Example #12
0
        public static MSBuildRootDocument Parse(
            string filename, ITextSource textSource, MSBuildRootDocument previous,
            MSBuildSchemaProvider schemaProvider, IRuntimeInformation runtimeInfo,
            CancellationToken token)
        {
            var xmlParser = new XmlParser(new XmlRootState(), true);

            try {
                xmlParser.Parse(textSource.CreateReader());
            } catch (Exception ex) {
                LoggingService.LogError("Unhandled error parsing xml document", ex);
            }

            var xdocument = xmlParser.Nodes.GetRoot();

            if (xdocument != null && xdocument.RootElement != null)
            {
                if (!xdocument.RootElement.IsEnded)
                {
                    xdocument.RootElement.End(xmlParser.Location);
                }
            }

            //FIXME: unfortunately the XML parser's regions only have line+col locations, not offsets
            //so we need to create an ITextDocument to extract tag bodies
            //we should fix this by changing the parser to use offsets for the tag locations
            ITextDocument textDoc = textSource as ITextDocument
                                    ?? TextEditorFactory.CreateNewDocument(textSource, filename, MSBuildTextEditorExtension.MSBuildMimeType);

            var propVals = new PropertyValueCollector(true);

            string projectPath = filename;

            var doc = new MSBuildRootDocument(filename)
            {
                XDocument          = xdocument,
                Text               = textDoc,
                RuntimeInformation = runtimeInfo
            };

            doc.Errors.AddRange(xmlParser.Errors);

            try {
                doc.Schema = previous?.Schema ?? schemaProvider.GetSchema(filename, null);
            } catch (Exception ex) {
                LoggingService.LogError("Error loading schema", ex);
            }

            var importedFiles = new HashSet <string> (StringComparer.OrdinalIgnoreCase)
            {
                filename
            };

            var taskBuilder = new TaskMetadataBuilder(doc);

            var extension = Path.GetExtension(filename);

            string MakeRelativeMSBuildPathAbsolute(string path)
            {
                var dir = Path.GetDirectoryName(doc.Filename);

                path = path.Replace('\\', Path.DirectorySeparatorChar);
                return(Path.GetFullPath(Path.Combine(dir, path)));
            }

            Import TryImportFile(string possibleFile)
            {
                try {
                    var fi = new FileInfo(possibleFile);
                    if (fi.Exists)
                    {
                        var imp = doc.GetCachedOrParse(importedFiles, previous, possibleFile, null, fi.LastWriteTimeUtc, projectPath, propVals, taskBuilder, schemaProvider, token);
                        doc.Imports.Add(possibleFile, imp);
                        return(imp);
                    }
                } catch (Exception ex) {
                    LoggingService.LogError($"Error importing '{possibleFile}'", ex);
                }
                return(null);
            }

            Import TryImportSibling(string ifHasThisExtension, string thenTryThisExtension)
            {
                if (string.Equals(ifHasThisExtension, extension, StringComparison.OrdinalIgnoreCase))
                {
                    var siblingFilename = Path.ChangeExtension(filename, thenTryThisExtension);
                    return(TryImportFile(siblingFilename));
                }
                return(null);
            }

            void TryImportIntellisenseImports(MSBuildSchema schema)
            {
                foreach (var intellisenseImport in schema.IntelliSenseImports)
                {
                    TryImportFile(MakeRelativeMSBuildPathAbsolute(intellisenseImport));
                }
            }

            try {
                //if this is a targets file, try to import the props _at the top_
                var propsImport = TryImportSibling(".targets", ".props");

                // this currently only happens in the root file
                // it's a quick hack to allow files to get some basic intellisense by
                // importing the files _that they themselves expect to be imported from_.
                // we also try to load them from the sibling props, as a paired targets/props
                // will likely share a schema file.
                var schema = doc.Schema ?? propsImport?.Document?.Schema;
                if (schema != null)
                {
                    TryImportIntellisenseImports(doc.Schema);
                }

                doc.Build(
                    xdocument, textDoc, runtimeInfo, propVals, taskBuilder,
                    (imp, sdk) => doc.ResolveImport(importedFiles, previous, projectPath, filename, imp, sdk, propVals, taskBuilder, schemaProvider, token)
                    );

                //if this is a props file, try to import the targets _at the bottom_
                var targetsImport = TryImportSibling(".props", ".targets");

                //and if we didn't load intellisense import already, try to load them from the sibling targets
                if (schema == null && targetsImport?.Document?.Schema != null)
                {
                    TryImportIntellisenseImports(targetsImport.Document.Schema);
                }
            } catch (Exception ex) {
                LoggingService.LogError($"Error building document '{projectPath}'", ex);
            }

            try {
                var binpath = doc.RuntimeInformation.GetBinPath();
                foreach (var t in Directory.GetFiles(binpath, "*.tasks"))
                {
                    doc.LoadTasks(importedFiles, previous, t, propVals, taskBuilder, schemaProvider, token);
                }
                foreach (var t in Directory.GetFiles(binpath, "*.overridetasks"))
                {
                    doc.LoadTasks(importedFiles, previous, t, propVals, taskBuilder, schemaProvider, token);
                }
            } catch (Exception ex) {
                LoggingService.LogError("Error resolving tasks", ex);
            }

            try {
                if (previous != null)
                {
                    // try to recover some values that may have been collected from the imports, as they
                    // will not have been re-evaluated
                    var fx = previous.Frameworks.FirstOrDefault();
                    if (fx != null)
                    {
                        propVals.Collect("TargetFramework", fx.GetShortFolderName());
                        propVals.Collect("TargetFrameworkVersion", FrameworkInfoProvider.FormatDisplayVersion(fx.Version));
                        propVals.Collect("TargetFrameworkIdentifier", fx.Framework);
                    }
                }
                doc.Frameworks = propVals.GetFrameworks();
            } catch (Exception ex) {
                LoggingService.LogError("Error determining project framework", ex);
                doc.Frameworks = new List <NuGetFramework> ();
            }

            try {
                //this has to run in a second pass so that it runs after all the schemas are loaded
                var validator = new MSBuildDocumentValidator();
                validator.Run(doc.XDocument, filename, textDoc, doc);
            } catch (Exception ex) {
                LoggingService.LogError("Error in validation", ex);
            }

            return(doc);
        }
Example #13
0
        IEnumerable <Import> ResolveImport(HashSet <string> importedFiles, MSBuildRootDocument oldDoc, string projectPath, string thisFilePath, string importExpr, string sdk, PropertyValueCollector propVals, TaskMetadataBuilder taskBuilder, MSBuildSchemaProvider schemaProvider, CancellationToken token)
        {
            //FIXME: add support for MSBuildUserExtensionsPath, the context does not currently support it
            if (importExpr.IndexOf("$(MSBuildUserExtensionsPath)", StringComparison.OrdinalIgnoreCase) > -1)
            {
                yield break;
            }

            //TODO: re-use these contexts instead of recreating them
            var importEvalCtx = MSBuildEvaluationContext.Create(RuntimeInformation, projectPath, thisFilePath);

            bool foundAny   = false;
            bool isWildcard = false;

            //the ToList is necessary because nested parses can alter the list between this yielding values
            foreach (var filename in importEvalCtx.EvaluatePathWithPermutation(importExpr, Path.GetDirectoryName(thisFilePath), propVals).ToList())
            {
                if (string.IsNullOrEmpty(filename))
                {
                    continue;
                }

                //dedup
                if (!importedFiles.Add(filename))
                {
                    foundAny = true;
                    continue;
                }

                //wildcards
                var wildcardIdx = filename.IndexOf('*');

                //arbitrary limit to skip improbably short values from bad evaluation
                const int MIN_WILDCARD_STAR_IDX    = 15;
                const int MIN_WILDCARD_PATTERN_IDX = 10;
                if (wildcardIdx > MIN_WILDCARD_STAR_IDX)
                {
                    isWildcard |= true;
                    var lastSlash = filename.LastIndexOf(Path.DirectorySeparatorChar);
                    if (lastSlash < MIN_WILDCARD_PATTERN_IDX)
                    {
                        continue;
                    }
                    if (lastSlash > wildcardIdx)
                    {
                        continue;
                    }

                    string [] files;
                    try {
                        var dir = filename.Substring(0, lastSlash);
                        if (!Directory.Exists(dir))
                        {
                            continue;
                        }

                        //finding the folder's enough for this to "count" as resolved even if there aren't any files in it
                        foundAny = true;

                        var pattern = filename.Substring(lastSlash + 1);

                        files = Directory.GetFiles(dir, pattern);
                    } catch (Exception ex) {
                        LoggingService.LogError($"Error evaluating wildcard in import candidate '{filename}'", ex);
                        continue;
                    }

                    foreach (var f in files)
                    {
                        Import wildImport;
                        try {
                            wildImport = GetCachedOrParse(importedFiles, oldDoc, f, sdk, File.GetLastWriteTimeUtc(f), projectPath, propVals, taskBuilder, schemaProvider, token);
                        } catch (Exception ex) {
                            LoggingService.LogError($"Error reading wildcard import candidate '{files}'", ex);
                            continue;
                        }
                        yield return(wildImport);
                    }

                    continue;
                }

                Import import;
                try {
                    var fi = new FileInfo(filename);
                    if (!fi.Exists)
                    {
                        continue;
                    }
                    import = GetCachedOrParse(importedFiles, oldDoc, filename, sdk, fi.LastWriteTimeUtc, projectPath, propVals, taskBuilder, schemaProvider, token);
                } catch (Exception ex) {
                    LoggingService.LogError($"Error reading import candidate '{filename}'", ex);
                    continue;
                }

                foundAny = true;
                yield return(import);

                continue;
            }

            // we skip logging for wildcards as these are generally extensibility points that are often unused
            // this is here (rather than being folded into the next condition) for ease of breakpointing
            if (isWildcard)
            {
                foundAny = true;
            }

            if (!foundAny)
            {
                if (oldDoc == null && failedImports.Add(importExpr))
                {
                    LoggingService.LogDebug($"Could not resolve MSBuild import '{importExpr}'");
                }
                yield return(new Import(importExpr, sdk, DateTime.MinValue));
            }
        }
Example #14
0
        public static MSBuildRootDocument Parse(
            string filename, ITextSource textSource, MSBuildRootDocument previous,
            MSBuildSchemaProvider schemaProvider, IRuntimeInformation runtimeInfo,
            CancellationToken token)
        {
            var xmlParser = new XmlParser(new XmlRootState(), true);

            try {
                xmlParser.Parse(textSource.CreateReader());
            } catch (Exception ex) {
                LoggingService.LogError("Unhandled error parsing xml document", ex);
            }

            var xdocument = xmlParser.Nodes.GetRoot();

            if (xdocument != null && xdocument.RootElement != null)
            {
                if (!xdocument.RootElement.IsEnded)
                {
                    xdocument.RootElement.End(xmlParser.Location);
                }
            }

            //FIXME: unfortunately the XML parser's regions only have line+col locations, not offsets
            //so we need to create an ITextDocument to extract tag bodies
            //we should fix this by changing the parser to use offsets for the tag locations
            ITextDocument textDoc = textSource as ITextDocument
                                    ?? TextEditorFactory.CreateNewDocument(textSource, filename, MSBuildTextEditorExtension.MSBuildMimeType);

            var propVals = new PropertyValueCollector(true);

            string projectPath = filename;

            var doc = new MSBuildRootDocument(filename);

            doc.XDocument          = xdocument;
            doc.Text               = textDoc;
            doc.RuntimeInformation = runtimeInfo;
            doc.Errors.AddRange(xmlParser.Errors);

            var importedFiles = new HashSet <string> (StringComparer.OrdinalIgnoreCase);

            importedFiles.Add(filename);

            var taskBuilder = new TaskMetadataBuilder(doc);

            try {
                doc.Build(
                    xdocument, textDoc, runtimeInfo, propVals, taskBuilder,
                    (imp, sdk) => doc.ResolveImport(importedFiles, previous, projectPath, filename, imp, sdk, propVals, taskBuilder, schemaProvider, token)
                    );
            } catch (Exception ex) {
                LoggingService.LogError("Error building document", ex);
            }

            try {
                var binpath = doc.RuntimeInformation.GetBinPath();
                foreach (var t in Directory.GetFiles(binpath, "*.tasks"))
                {
                    doc.LoadTasks(importedFiles, previous, t, propVals, taskBuilder, schemaProvider, token);
                }
                foreach (var t in Directory.GetFiles(binpath, "*.overridetasks"))
                {
                    doc.LoadTasks(importedFiles, previous, t, propVals, taskBuilder, schemaProvider, token);
                }
            } catch (Exception ex) {
                LoggingService.LogError("Error resolving tasks", ex);
            }


            try {
                if (previous != null)
                {
                    // try to recover some values that may have been collected from the imports, as they
                    // will not have been re-evaluated
                    var fx = previous.Frameworks.FirstOrDefault();
                    if (fx != null)
                    {
                        propVals.Collect("TargetFramework", fx.GetShortFolderName());
                        propVals.Collect("TargetFrameworkVersion", FrameworkInfoProvider.FormatDisplayVersion(fx.Version));
                        propVals.Collect("TargetFrameworkIdentifier", fx.Framework);
                    }
                }
                doc.Frameworks = propVals.GetFrameworks();
            } catch (Exception ex) {
                LoggingService.LogError("Error determining project framework", ex);
                doc.Frameworks = new List <NuGetFramework> ();
            }

            try {
                doc.Schema = previous?.Schema ?? schemaProvider.GetSchema(filename, null);
            } catch (Exception ex) {
                LoggingService.LogError("Error loading schema", ex);
            }

            try {
                //this has to run in a second pass so that it runs after all the schemas are loaded
                var validator = new MSBuildDocumentValidator();
                validator.Run(doc.XDocument, filename, textDoc, doc);
            } catch (Exception ex) {
                LoggingService.LogError("Error in validation", ex);
            }

            return(doc);
        }
Example #15
0
 public void Run(MSBuildRootDocument doc)
 {
     Run(doc.XDocument, doc.Filename, doc.Text, doc);
 }