/// <summary> /// Loads the Q# data structures in a referenced assembly given the Uri to that assembly, /// and returns the loaded content as out parameter. /// Returns false if some of the content could not be loaded successfully, /// possibly because the referenced assembly has been compiled with an older compiler version. /// If onDeserializationException is specified, invokes the given action on any exception thrown during deserialization. /// Throws the corresponding exceptions if the information cannot be extracted. /// </summary> /// <exception cref="FileNotFoundException"><paramref name="asm"/> does not exist.</exception> /// <exception cref="ArgumentException"><paramref name="asm"/> is not an absolute file URI.</exception> public static bool LoadReferencedAssembly(Uri asm, out References.Headers headers, bool ignoreDllResources = false, Action <Exception>?onDeserializationException = null) { var id = CompilationUnitManager.GetFileId(asm); if (!File.Exists(asm.LocalPath)) { throw new FileNotFoundException($"The file '{asm.LocalPath}' given to the assembly loader does not exist."); } using var stream = File.OpenRead(asm.LocalPath); using var assemblyFile = new PEReader(stream); if (ignoreDllResources || !FromResource(assemblyFile, out var compilation, onDeserializationException)) { PerformanceTracking.TaskStart(PerformanceTracking.Task.HeaderAttributesLoading); var attributes = LoadHeaderAttributes(assemblyFile); PerformanceTracking.TaskEnd(PerformanceTracking.Task.HeaderAttributesLoading); PerformanceTracking.TaskStart(PerformanceTracking.Task.ReferenceHeadersCreation); headers = new References.Headers(id, attributes); PerformanceTracking.TaskEnd(PerformanceTracking.Task.ReferenceHeadersCreation); return(ignoreDllResources || !attributes.Any()); // just means we have no references } PerformanceTracking.TaskStart(PerformanceTracking.Task.ReferenceHeadersCreation); headers = new References.Headers(id, compilation?.Namespaces ?? ImmutableArray <QsNamespace> .Empty); PerformanceTracking.TaskEnd(PerformanceTracking.Task.ReferenceHeadersCreation); return(true); }
/// <summary> /// Compiles the given code. /// If the operations defined in this code are already defined /// in existing Snippets, those Snippets are skipped. /// If successful, this updates the AssemblyInfo /// with the new operations found in the Snippet. /// If errors are found during compilation, a `CompilationErrorsException` is triggered /// with the list of errors found. /// If successful, the list of snippets is updated to include those that were part of the /// compilation and it will return a new Snippet with the warnings and Q# elements /// reported by the compiler. /// </summary> public Snippet Compile(string code) { if (string.IsNullOrWhiteSpace(code)) { throw new ArgumentNullException(nameof(code)); } var duration = Stopwatch.StartNew(); // We add exactly one line of boilerplate code at the beginning of each snippet, // so tell the logger to subtract one from all displayed line numbers. var logger = new QSharpLogger(Logger, lineNrOffset: -1); try { var snippets = SelectSnippetsToCompile(code).ToArray(); var assembly = Compiler.BuildSnippets(snippets, _metadata.Value, logger, Path.Combine(Workspace.CacheFolder, "__snippets__.dll")); if (logger.HasErrors) { throw new CompilationErrorsException(logger.Errors.ToArray()); } foreach (var entry in Compiler.IdentifyOpenedNamespaces(code)) { Compiler.AutoOpenNamespaces[entry.Key] = entry.Value; } // populate the original snippet with the results of the compilation: Snippet populate(Snippet s) => new Snippet() { id = string.IsNullOrWhiteSpace(s.id) ? Guid.NewGuid().ToString() : s.id, code = s.code, warnings = logger.Logs .Where(m => m.Source == CompilationUnitManager.GetFileId(s.Uri).Value) .Select(logger.Format) .ToArray(), Elements = assembly?.SyntaxTree? .SelectMany(ns => ns.Elements) .Where(c => c.SourceFile() == CompilationUnitManager.GetFileId(s.Uri).Value) .ToArray() }; AssemblyInfo = assembly; Items = snippets.Select(populate).ToArray(); return(Items.Last()); } finally { duration.Stop(); var status = logger.HasErrors ? "error" : "ok"; var errorIds = logger.ErrorIds.ToArray(); SnippetCompiled?.Invoke(this, new SnippetCompiledEventArgs(status, errorIds, Compiler.AutoOpenNamespaces.Keys.ToArray(), duration.Elapsed)); } }
/// <summary> /// Compiles the given code. /// If the operations defined in this code are already defined /// in existing Snippets, those Snippets are skipped. /// If successful, this updates the AssemblyInfo /// with the new operations found in the Snippet. /// If errors are found during compilation, a `CompilationErrorsException` is triggered /// with the list of errors found. /// If successful, the list of snippets is updated to include those that were part of the /// compilation and it will return a new Snippet with the warnings and Q# elements /// reported by the compiler. /// </summary> public Snippet Compile(string code) { if (string.IsNullOrWhiteSpace(code)) { throw new ArgumentNullException(nameof(code)); } var duration = Stopwatch.StartNew(); var logger = new QSharpLogger(Logger); try { var snippets = SelectSnippetsToCompile(code).ToArray(); var assembly = Compiler.BuildSnippets(snippets, _metadata.Value, logger, Path.Combine(Workspace.CacheFolder, "__snippets__.dll")); if (logger.HasErrors) { throw new CompilationErrorsException(logger.Errors.ToArray()); } // populate the original snippet with the results of the compilation: Snippet populate(Snippet s) => new Snippet() { id = string.IsNullOrWhiteSpace(s.id) ? Guid.NewGuid().ToString() : s.id, code = s.code, warnings = logger.Logs .Where(m => m.Source == CompilationUnitManager.GetFileId(s.Uri).Value) .Select(logger.Format) .ToArray(), Elements = assembly?.SyntaxTree? .SelectMany(ns => ns.Elements) .Where(c => c.SourceFile() == CompilationUnitManager.GetFileId(s.Uri).Value) .ToArray() }; AssemblyInfo = assembly; Items = snippets.Select(populate).ToArray(); return(Items.Last()); } finally { duration.Stop(); var status = logger.HasErrors ? "error" : "ok"; var errorIds = logger.ErrorIds.ToArray(); SnippetCompiled?.Invoke(this, new SnippetCompiledEventArgs(status, errorIds, duration.Elapsed)); } }
/// <summary> /// Returns the workspace edit that describes the changes to be done if the symbol at the given position - if any - is renamed to the given name. /// Returns null if no symbol exists at the specified position, /// or if some parameters are unspecified (null), /// or if the specified position is not a valid position within the file. /// </summary> public static WorkspaceEdit?Rename(this FileContentManager file, CompilationUnit compilation, Position position, string newName) { if (newName == null || file == null) { return(null); } var found = file.TryGetReferences(compilation, position, out var declLocation, out var locations); if (!found) { return(null); } if (declLocation != null) { locations = new[] { declLocation }.Concat(locations); } var changes = locations.ToLookup(loc => loc.Uri, loc => new TextEdit { Range = loc.Range, NewText = newName }); return(new WorkspaceEdit { DocumentChanges = changes .Select(change => new TextDocumentEdit { TextDocument = new VersionedTextDocumentIdentifier { Uri = change.Key, Version = 1 }, // setting version to null here won't work in VS Code ... Edits = change.ToArray() }) .ToArray(), Changes = changes.ToDictionary( items => CompilationUnitManager.GetFileId(items.Key), items => items.ToArray()) }); }