private void DoTaskInternal() { Debug.Assert(_taskDoneEvent != null); Action finalAction; if (!IsDisposed) { object result = null; try { result = _taskAction(); } catch (Exception ex) { Debug.Fail(String.Format(CultureInfo.CurrentCulture, "Background task exception: {0}.\nInner exception: {1}\nInner exception callstack: {2}", ex.Message, ex.InnerException != null ? ex.InnerException.Message : "(none)", ex.InnerException != null ? ex.InnerException.StackTrace : "(none)")); result = ex; } finally { finalAction = () => UIThreadCompletedCallback(result); } } else { finalAction = () => UIThreadCanceledCallback(null); } _taskDoneEvent.Set(); EditorShell.DispatchOnUIThread(finalAction); }
private async Task InsertFunctionBraces(SnapshotPoint position, string name) { bool function = await IsFunction(name); if (function) { EditorShell.DispatchOnUIThread(() => { if (TextView.TextBuffer.CurrentSnapshot.Version.VersionNumber == position.Snapshot.Version.VersionNumber) { TextView.TextBuffer.Insert(position.Position, "()"); TextView.Caret.MoveTo(new SnapshotPoint(TextView.TextBuffer.CurrentSnapshot, position.Position + 1)); EditorShell.DispatchOnUIThread(() => TriggerSignatureHelp()); } }); } }
protected void SubmitToInteractive(string command, CancellationToken cancellationToken) { var sessionProvider = EditorShell.Current.ExportProvider.GetExportedValue <IRSessionProvider>(); var session = sessionProvider.GetOrCreate(GuidList.InteractiveWindowRSessionGuid); if (session != null && session.IsHostRunning) { _runningAction = Task.Run(async() => { try { using (var eval = await session.BeginInteractionAsync(isVisible: true, cancellationToken: cancellationToken)) { await eval.RespondAsync(command); } } finally { EditorShell.DispatchOnUIThread(() => _runningAction = null); } }); _runningAction.SilenceException <OperationCanceledException>() .SilenceException <MessageTransportException>(); } }
/// <summary> /// Fetches help on the function from R asynchronously. /// When function data is obtained, parsed and the function /// index is updated, method invokes <see cref="infoReadyCallback"/> /// callback passing the specified parameter. Callback method can now /// fetch function information from the index. /// </summary> private static void GetFunctionInfoFromEngineAsync(string functionName, string packageName, Action <object> infoReadyCallback = null, object parameter = null) { _functionRdDataProvider.GetFunctionRdData( functionName, packageName, (string rdData) => { IReadOnlyList <IFunctionInfo> functionInfos = GetFunctionInfosFromRd(rdData); foreach (IFunctionInfo info in functionInfos) { _functionToInfoMap[info.Name] = info; } if (!_functionToInfoMap.ContainsKey(functionName)) { if (functionInfos.Count > 0) { // RD doesn't contain the requested function. // e.g. as.Date.character has RD for as.Date but not itself // without its own named info, this will request indefinitely many times // as workaround, add the first info with functionName _functionToInfoMap[functionName] = functionInfos[0]; } else { // TODO: add some stub function info here to prevent subsequent calls for the same function as we already know the call will fail. } } if (infoReadyCallback != null) { EditorShell.DispatchOnUIThread(() => { infoReadyCallback(parameter); }); } }); }
/// <summary> /// Main asyncronous task body /// </summary> void ProcessTextChanges(TextChange changeToProcess, bool async, Func <bool> isCancelledCallback) { lock (_disposeLock) { if (_editorTree == null || _disposed || isCancelledCallback()) { return; } EditorTreeChangeCollection treeChanges = null; // Cache id since it can change if task is canceled long taskId = TaskId; try { AstRoot rootNode; // We only need read lock since changes will be applied // from the main thread if (async) { rootNode = _editorTree.AcquireReadLock(_treeUserId); } else { rootNode = _editorTree.GetAstRootUnsafe(); } treeChanges = new EditorTreeChangeCollection(changeToProcess.Version, changeToProcess.FullParseRequired); TextChangeProcessor changeProcessor = new TextChangeProcessor(_editorTree, rootNode, isCancelledCallback); bool fullParseRequired = changeToProcess.FullParseRequired; if (fullParseRequired) { changeProcessor.FullParse(treeChanges, changeToProcess.NewTextProvider); } else { changeProcessor.ProcessChange(changeToProcess, treeChanges); } } finally { if (async && _editorTree != null) { _editorTree.ReleaseReadLock(_treeUserId); } } // Lock should be released at this point since actual application // of tree changes is going to be happen from the main thread. if (!isCancelledCallback() && treeChanges != null) { // Queue results for the main thread application. This must be done before // signaling that the task is complete since if EnsureProcessingComplete // is waiting it will want to apply changes itself rather than wait for // the DispatchOnUIThread to go though and hence it will need all changes // stored and ready for application. _backgroundParsingResults.Enqueue(treeChanges); } // Signal task complete now so if main thread is waiting // it can proceed and appy the changes immediately. SignalTaskComplete(taskId); if (_backgroundParsingResults.Count > 0) { _uiThreadTransitionRequestTime = DateTime.UtcNow; // It is OK to post results while main thread might be working // on them since if if it does, by the time posted request comes // queue will already be empty. if (async) { // Post request to apply tree changes to the main thread. // This must NOT block or else task will never enter 'RanToCompletion' state. EditorShell.DispatchOnUIThread(() => ApplyBackgroundProcessingResults()); } else { // When processing is synchronous, apply changes and fire events right away. ApplyBackgroundProcessingResults(); } } } }
private static bool LoadIndex() { ConcurrentDictionary <string, string> functionToPackageMap = new ConcurrentDictionary <string, string>(); ConcurrentDictionary <string, IFunctionInfo> functionToInfoMap = new ConcurrentDictionary <string, IFunctionInfo>(); ConcurrentDictionary <string, BlockingCollection <INamedItemInfo> > packageToFunctionsMap = new ConcurrentDictionary <string, BlockingCollection <INamedItemInfo> >(); bool loaded = false; // ~/Documents/R/RTVS/FunctionsIndex.dx -> function to package map, also contains function description // ~/Documents/R/RTVS/[PackageName]/[FunctionName].sig -> function signatures // Function index format: // Each line is a triplet of function name followed // by the package name followed by the function description // There are no tabs in the function description. try { if (File.Exists(IndexFilePath)) { using (StreamReader sr = new StreamReader(IndexFilePath)) { char[] separator = new char[] { '\t' }; while (true) { string line = sr.ReadLine(); if (line == null) { break; } string[] parts = line.Split(separator, StringSplitOptions.RemoveEmptyEntries); if (parts.Length == 3) { string functionName = parts[0]; string packageName = parts[1]; string functionDescription = parts[2]; if (functionName == "completed" && packageName == "completed" && functionDescription == "completed") { loaded = true; break; } functionToPackageMap[functionName] = packageName; BlockingCollection <INamedItemInfo> functions; if (!packageToFunctionsMap.TryGetValue(packageName, out functions)) { functions = new BlockingCollection <INamedItemInfo>(); packageToFunctionsMap[packageName] = functions; } functions.Add(new NamedItemInfo(functionName, functionDescription)); } } } } } catch (IOException) { } if (!loaded) { return(false); } if (LoadFunctions(functionToPackageMap)) { EditorShell.DispatchOnUIThread(() => { _functionToPackageMap = functionToPackageMap; _packageToFunctionsMap = packageToFunctionsMap; _functionToInfoMap = functionToInfoMap; }); } return(loaded); }