From 3ea74ac9db29472e6e4f1d8ea4ee9bf99e8767ed Mon Sep 17 00:00:00 2001 From: Roland Grunberg Date: Tue, 23 Jan 2024 19:31:32 -0500 Subject: [PATCH] Introduce capability to check if workspace has exited improperly. - .snap files under the workspace metadata location indicate in improper workspace shutdown Signed-off-by: Roland Grunberg --- USAGE_DATA.md | 4 ++-- src/extension.ts | 53 ++++++++++++++++++++++++++++++------------------ src/utils.ts | 10 +++++++-- 3 files changed, 43 insertions(+), 24 deletions(-) diff --git a/USAGE_DATA.md b/USAGE_DATA.md index fb5c3a6e9..1a771627f 100644 --- a/USAGE_DATA.md +++ b/USAGE_DATA.md @@ -20,9 +20,9 @@ vscode-java has opt-in telemetry collection, provided by [vscode-redhat-telemetr * Information about the following settings. In the case of settings that store a well defined value (eg. path/url/string), we simply collect whether the setting has been set. * `java.settings.url`, `java.format.settings.url`, `java.quickfix.showAt`, `java.symbols.includeSourceMethodDeclarations`, `java.completion.guessMethodArguments`, `java.completion.postfix.enabled`, `java.cleanup.actionsOnSave`, `java.sharedIndexes.enabled`, `java.inlayHints.parameterNames.enabled`, `java.server.launchMode`, `java.autobuild.enabled` * The extension name and the choice made when a recommendation to install a 3rd party extension is proposed - * The name of Java commands being manually executed, and any resulting errors + * The name of Java commands being executed, and any resulting errors * The number of results (eg. 20), whether an error occured (eg. false), and duration (in milliseconds) when code assist is activated - + ## What's included in the general telemetry data Please see the diff --git a/src/extension.ts b/src/extension.ts index 398b6a00f..466d66d89 100644 --- a/src/extension.ts +++ b/src/extension.ts @@ -1,5 +1,6 @@ 'use strict'; +import { TelemetryService } from '@redhat-developer/vscode-redhat-telemetry/lib'; import * as chokidar from 'chokidar'; import * as fs from 'fs'; import * as fse from 'fs-extra'; @@ -9,35 +10,34 @@ import { CodeActionContext, commands, ConfigurationTarget, Diagnostic, env, Even import { CancellationToken, CodeActionParams, CodeActionRequest, Command, CompletionRequest, DidChangeConfigurationNotification, ExecuteCommandParams, ExecuteCommandRequest, LanguageClientOptions, RevealOutputChannelOn } from 'vscode-languageclient'; import { LanguageClient } from 'vscode-languageclient/node'; import { apiManager } from './apiManager'; +import { BuildFileSelector, cleanupProjectPickerCache, PICKED_BUILD_FILES } from './buildFilesSelector'; import { ClientErrorHandler } from './clientErrorHandler'; import { Commands } from './commands'; +import { getMessage } from './errorUtils'; import { ClientStatus, ExtensionAPI, TraceEvent } from './extension.api'; import * as fileEventHandler from './fileEventHandler'; +import { JavaClassEditorProvider } from './javaClassEditor'; import { getSharedIndexCache, HEAP_DUMP_LOCATION, prepareExecutable } from './javaServerStarter'; +import { loadSupportedJreNames } from './jdkUtils'; import { initializeLogFile, logger } from './log'; import { cleanupLombokCache } from "./lombokSupport"; import { markdownPreviewProvider } from "./markdownPreviewProvider"; import { OutputInfoCollector } from './outputInfoCollector'; +import { pasteFile } from './pasteAction'; import { collectJavaExtensions, getBundlesToReload, isContributedPartUpdated } from './plugin'; import { registerClientProviders } from './providerDispatcher'; import { initialize as initializeRecommendation } from './recommendation'; import * as requirements from './requirements'; import { languageStatusBarProvider } from './runtimeStatusBarProvider'; import { serverStatusBarProvider } from './serverStatusBarProvider'; -import { ACTIVE_BUILD_TOOL_STATE, cleanWorkspaceFileName, getJavaServerMode, handleTextDocumentChanges, getImportMode, onConfigurationChange, ServerMode, ImportMode } from './settings'; +import { activationProgressNotification } from "./serverTaskPresenter"; +import { ACTIVE_BUILD_TOOL_STATE, cleanWorkspaceFileName, getImportMode, getJavaServerMode, handleTextDocumentChanges, ImportMode, onConfigurationChange, ServerMode } from './settings'; import { snippetCompletionProvider } from './snippetCompletionProvider'; -import { JavaClassEditorProvider } from './javaClassEditor'; import { StandardLanguageClient } from './standardLanguageClient'; import { SyntaxLanguageClient } from './syntaxLanguageClient'; -import { convertToGlob, deleteClientLog, deleteDirectory, ensureExists, getBuildFilePatterns, getExclusionGlob, getInclusionPatternsFromNegatedExclusion, getJavaConfig, getJavaConfiguration, hasBuildToolConflicts, resolveActualCause } from './utils'; -import glob = require('glob'); import { Telemetry } from './telemetry'; -import { getMessage } from './errorUtils'; -import { TelemetryService } from '@redhat-developer/vscode-redhat-telemetry/lib'; -import { activationProgressNotification } from "./serverTaskPresenter"; -import { loadSupportedJreNames } from './jdkUtils'; -import { BuildFileSelector, PICKED_BUILD_FILES, cleanupProjectPickerCache } from './buildFilesSelector'; -import { pasteFile } from './pasteAction'; +import { convertToGlob, deleteClientLog, deleteDirectory, ensureExists, getBuildFilePatterns, getExclusionGlob, getInclusionPatternsFromNegatedExclusion, getJavaConfig, getJavaConfiguration, hasBuildToolConflicts, hasWorkspaceImproperlyExited, resolveActualCause } from './utils'; +import glob = require('glob'); const syntaxClient: SyntaxLanguageClient = new SyntaxLanguageClient(); const standardClient: StandardLanguageClient = new StandardLanguageClient(); @@ -328,16 +328,29 @@ export async function activate(context: ExtensionContext): Promise } })); - if (cleanWorkspaceExists) { - const data = {}; - try { - cleanupLombokCache(context); - cleanupProjectPickerCache(context); - deleteDirectory(workspacePath); - deleteDirectory(syntaxServerWorkspacePath); - } catch (error) { - data['error'] = getMessage(error); - window.showErrorMessage(`Failed to delete ${workspacePath}: ${error}`); + const workspaceImproperlyExited = hasWorkspaceImproperlyExited(workspacePath); + if (cleanWorkspaceExists || workspaceImproperlyExited) { + if (workspaceImproperlyExited) { + apiManager.fireTraceEvent({ + name: 'java.ls.error.notification', + properties: { + message: "The workspace exited with unsaved changes in the previous session.", + }, + }); + } + const data = { + reason: workspaceImproperlyExited ? 'automatic' : 'manual' + }; + if (cleanWorkspaceExists) { + try { + cleanupLombokCache(context); + cleanupProjectPickerCache(context); + deleteDirectory(workspacePath); + deleteDirectory(syntaxServerWorkspacePath); + } catch (error) { + data['error'] = getMessage(error); + window.showErrorMessage(`Failed to delete ${workspacePath}: ${error}`); + } } await Telemetry.sendTelemetry(Commands.CLEAN_WORKSPACE, data); } diff --git a/src/utils.ts b/src/utils.ts index c3ff4805f..5dc5d681e 100644 --- a/src/utils.ts +++ b/src/utils.ts @@ -1,11 +1,12 @@ 'use strict'; import * as fs from 'fs'; +import { IJavaRuntime } from 'jdk-utils'; import * as path from 'path'; -import { workspace, WorkspaceConfiguration, commands, Uri, version } from 'vscode'; +import { Uri, WorkspaceConfiguration, commands, version, workspace } from 'vscode'; import { Commands } from './commands'; -import { IJavaRuntime } from 'jdk-utils'; import { getSupportedJreNames, listJdks, sortJdksBySource, sortJdksByVersion } from './jdkUtils'; +import glob = require('glob'); export function getJavaConfiguration(): WorkspaceConfiguration { return workspace.getConfiguration('java'); @@ -316,3 +317,8 @@ export function resolveActualCause(callstack: any): any { return callstack; } + +export function hasWorkspaceImproperlyExited(workspacePath: string): boolean { + const result = new glob.GlobSync(`${workspacePath}/.metadata/.plugins/org.eclipse.core.resources/*.snap`); + return result.found.length > 0; +} \ No newline at end of file