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); }
public static List <MSBuildNavigationResult> ResolveAll(MSBuildRootDocument doc, int offset, int length) { var visitor = new MSBuildNavigationVisitor(); visitor.Run(doc, offset, length); return(visitor.Navigations); }
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(); }
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); }
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))); }
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); } } } } }
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); } }
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); }
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); }
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); }
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)); } }
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); }
public void Run(MSBuildRootDocument doc) { Run(doc.XDocument, doc.Filename, doc.Text, doc); }