Example #1
0
        // routines related to tracking the editor state

        /// <summary>
        /// To be called whenever a file is opened in the editor.
        /// Does nothing if the given file is listed as to be ignored.
        /// Otherwise publishes suitable diagnostics for it.
        /// Invokes the given Action showError with a suitable message if the given file cannot be loaded.
        /// Invokes the given Action logError with a suitable message if the given file cannot be associated with a compilation unit,
        /// or if the given file is already listed as being open in the editor.
        /// Throws an ArgumentException if the uri of the given text document identifier is null or not an absolute file uri.
        /// Throws an ArgumentNullException if the given content is null.
        /// </summary>
        internal Task OpenFileAsync(TextDocumentItem textDocument,
                                    Action <string, MessageType> showError = null, Action <string, MessageType> logError = null)
        {
            if (!ValidFileUri(textDocument?.Uri))
            {
                throw new ArgumentException("invalid text document identifier");
            }
            if (textDocument.Text == null)
            {
                throw new ArgumentNullException(nameof(textDocument.Text));
            }
            _ = this.Projects.ManagerTaskAsync(textDocument.Uri, (manager, associatedWithProject) =>
            {
                if (IgnoreFile(textDocument.Uri))
                {
                    return;
                }
                var newManager = CompilationUnitManager.InitializeFileManager(textDocument.Uri, textDocument.Text, this.Publish, ex =>
                {
                    showError?.Invoke($"Failed to load file '{textDocument.Uri.LocalPath}'", MessageType.Error);
                    manager.LogException(ex);
                });

                // Currently it is not possible to handle both the behavior of VS and VS Code for changes on disk in a manner that will never fail.
                // To mitigate the impact of failures we choose to just log them as info.
                var file = this.OpenFiles.GetOrAdd(textDocument.Uri, newManager);
                if (file != newManager) // this may be the case (depending on the editor) e.g. when opening a version control diff ...
                {
                    showError?.Invoke($"Version control and opening multiple versions of the same file in the editor are currently not supported. \n" +
                                      $"Intellisense has been disable for the file '{textDocument.Uri.LocalPath}'. An editor restart is required to enable intellisense again.", MessageType.Error);
                    #if DEBUG
                    if (showError == null)
                    {
                        logError?.Invoke("Attempting to open a file that is already open in the editor.", MessageType.Error);
                    }
                    #endif

                    this.IgnoreEditorUpdatesFor(textDocument.Uri);
                    this.OpenFiles.TryRemove(textDocument.Uri, out FileContentManager _);
                    if (!associatedWithProject)
                    {
                        _ = manager.TryRemoveSourceFileAsync(textDocument.Uri);
                    }
                    this.Publish(new PublishDiagnosticParams {
                        Uri = textDocument.Uri, Diagnostics = new Diagnostic[0]
                    });
                    return;
                }

                if (!associatedWithProject)
                {
                    logError?.Invoke(
                        $"The file {textDocument.Uri.LocalPath} is not associated with a compilation unit. Only syntactic diagnostics are generated."
                        , MessageType.Info);
                }
                _ = manager.AddOrUpdateSourceFileAsync(file);
            });
            // reloading from disk in case we encountered a file already open error above
            return(this.Projects.SourceFileChangedOnDiskAsync(textDocument.Uri, GetOpenFile)); // NOTE: relies on that the manager task is indeed executed first!
        }
Example #2
0
        /// <summary>
        /// Used to reload the file content when a file is saved.
        /// Does nothing if the given file is listed as to be ignored.
        /// Expects to get the entire content of the file at the time of saving as argument.
        /// Throws an ArgumentException if the uri of the given text document identifier is null or not an absolute file uri.
        /// Throws an ArgumentNullException if the given content is null.
        /// </summary>
        internal Task SaveFileAsync(TextDocumentIdentifier textDocument, string fileContent)
        {
            if (!ValidFileUri(textDocument?.Uri))
            {
                throw new ArgumentException("invalid text document identifier");
            }
            if (fileContent == null)
            {
                throw new ArgumentNullException(nameof(fileContent));
            }
            return(this.Projects.ManagerTaskAsync(textDocument.Uri, (manager, __) =>
            {
                if (IgnoreFile(textDocument.Uri))
                {
                    return;
                }

                // Currently it is not possible to handle both the behavior of VS and VS Code for changes on disk in a manner that will never fail.
                // To mitigate the impact of failures we choose to ignore them silently and do our best to recover.
                if (!this.OpenFiles.TryGetValue(textDocument.Uri, out var file))
                {
                    file = CompilationUnitManager.InitializeFileManager(textDocument.Uri, fileContent, this.Publish, manager.LogException);
                    this.OpenFiles.TryAdd(textDocument.Uri, file);
                    _ = manager.AddOrUpdateSourceFileAsync(file);
                }
                else
                {
                    _ = manager.AddOrUpdateSourceFileAsync(file, fileContent);  // let's reload the file content on saving
                }
            }));
        }
Example #3
0
        private FileContentManager InitializeFileManager(IEnumerable <string> examples, QsCompilation compilation, string nsName = null)
        {
            var(pre, post) = ($"namespace {nsName ?? DefaultNamespaceName}{{ {Environment.NewLine}", $"{Environment.NewLine}}}");
            var openDirs = String.Join(Environment.NewLine,
                                       OpenedForTesting
                                       .Where(nsName => ContainsNamespace(compilation, nsName))
                                       .Select(nsName => $"open {nsName};"));

            string WrapInNamespace(string example) => pre + openDirs + example + post;

            examples = examples.Where(ex => !String.IsNullOrWhiteSpace(ex));
            var sourceCode = String.Join(Environment.NewLine, examples.Select(WrapInNamespace));

            var sourceName = NonNullable <string> .New(Path.GetFullPath($"{nsName}{CodeSource}"));

            return(CompilationUnitManager.TryGetUri(sourceName, out var sourceUri)
                ? CompilationUnitManager.InitializeFileManager(sourceUri, sourceCode)
                : null);
        }