diff --git a/package.json b/package.json index 8dd85f1c..8ab92e78 100644 --- a/package.json +++ b/package.json @@ -117,6 +117,11 @@ "name": "DeepCode Analysis", "when": "!deepcode:error && deepcode:loggedIn && deepcode:uploadApproved && deepcode:workspaceFound" }, + { + "id": "deepcode.views.actions", + "name": "Actions", + "when": "!deepcode:error && deepcode:loggedIn && deepcode:uploadApproved && deepcode:workspaceFound" + }, { "id": "deepcode.views.support", "name": "Help & feedback" @@ -149,6 +154,26 @@ { "view": "deepcode.views.analysis", "contents": "DeepCode analyzed your code and found no issue! 🎉" + }, + { + "view": "deepcode.views.actions", + "contents": "You are currently running DeepCode in manual mode. You are in control, no automated actions from our side.\n[Analyze now](command:deepcode.start)\n[Switch to auto-scan mode](command:deepcode.setmode?%5B%22auto%22%5D)", + "when": "deepcode:mode == 'manual'" + }, + { + "view": "deepcode.views.actions", + "contents": "DeepCode analysis is currently paused.\n[Unpause](command:deepcode.setmode?%5B%22auto%22%5D)", + "when": "deepcode:mode == 'paused'" + }, + { + "view": "deepcode.views.actions", + "contents": "You are currently running DeepCode in a throttled mode - it scans your code every 30 minutes if it detects changes in your files.\n[Analyze now](command:deepcode.start)\n[Switch to auto-scan mode](command:deepcode.setmode?%5B%22auto%22%5D)", + "when": "deepcode:mode == 'throttled'" + }, + { + "view": "deepcode.views.actions", + "contents": "You are currently running DeepCode in a fully automated mode. It scans your code for issues when you save a file.\nNeed to take control?\n[Pause DeepCode for 30 minutes](command:deepcode.setmode?%5B%22paused%22%5D)\n[Switch to manual scan mode](command:deepcode.setmode?%5B%22manual%22%5D)\n[Switch to throttled scan mode](command:deepcode.setmode?%5B%22throttled%22%5D)", + "when": "deepcode:mode != 'manual' && deepcode:mode != 'paused' && deepcode:mode != 'throttled'" } ], "menus": { diff --git a/src/deepcode/DeepCodeExtension.ts b/src/deepcode/DeepCodeExtension.ts index 2e0f2b69..c0b49e59 100644 --- a/src/deepcode/DeepCodeExtension.ts +++ b/src/deepcode/DeepCodeExtension.ts @@ -5,7 +5,8 @@ import DeepCode from "../interfaces/DeepCodeInterfaces"; import DeepCodeLib from "./lib/modules/DeepCodeLib"; import { - DEEPCODE_START_COMMAND, + DEEPCODE_START_COMMAND, + DEEPCODE_SETMODE_COMMAND, DEEPCODE_SETTINGS_COMMAND, DEEPCODE_DCIGNORE_COMMAND, DEEPCODE_LOGIN, @@ -97,7 +98,14 @@ class DeepCodeExtension extends DeepCodeLib implements DeepCode.ExtensionInterfa ) ) ); - + + context.subscriptions.push( + vscode.commands.registerCommand( + DEEPCODE_SETMODE_COMMAND, + this.setMode.bind(this) + ) + ); + context.subscriptions.push( vscode.commands.registerCommand( DEEPCODE_SETTINGS_COMMAND, diff --git a/src/deepcode/constants/commands.ts b/src/deepcode/constants/commands.ts index 0fb266da..86cbf80d 100644 --- a/src/deepcode/constants/commands.ts +++ b/src/deepcode/constants/commands.ts @@ -6,6 +6,7 @@ export const VSCODE_ADD_COMMENT_COMMAND = "editor.action.addCommentLine"; export const DEEPCODE_START_COMMAND = "deepcode.start"; export const DEEPCODE_LOGIN = "deepcode.login"; export const DEEPCODE_APPROVE = "deepcode.approve"; +export const DEEPCODE_SETMODE_COMMAND = "deepcode.setmode"; export const DEEPCODE_SETTINGS_COMMAND = "deepcode.settings"; export const DEEPCODE_IGNORE_ISSUES_COMMAND = "deepcode.ignoreissues"; export const DEEPCODE_DCIGNORE_COMMAND = "deepcode.dcignore"; diff --git a/src/deepcode/constants/general.ts b/src/deepcode/constants/general.ts index 90df9d3c..a50e6367 100644 --- a/src/deepcode/constants/general.ts +++ b/src/deepcode/constants/general.ts @@ -4,5 +4,7 @@ export const DEEPCODE_EXTENSION_NAME = "deepcode"; export const ALLOWED_PAYLOAD_SIZE = 1024 * 1024 * 4; // max payload size of 4MB in bytes export const MAX_CONNECTION_RETRIES = 5; // max number of automatic retries before showing an error export const IDE_NAME = "vscode"; -export const EXECUTION_DEBOUNCE_INTERVAL = 1000; -export const REFRESH_VIEW_DEBOUNCE_INTERVAL = 200; +export const EXECUTION_DEBOUNCE_INTERVAL = 1000; // 1 second +export const EXECUTION_THROTTLING_INTERVAL = 1000 * 10;// * 60 * 30; // 30 minutes +export const EXECUTION_PAUSE_INTERVAL = 1000 * 60 * 30; // 30 minutes +export const REFRESH_VIEW_DEBOUNCE_INTERVAL = 200; // 200 milliseconds diff --git a/src/deepcode/constants/views.ts b/src/deepcode/constants/views.ts index cba18fe2..4f6326db 100644 --- a/src/deepcode/constants/views.ts +++ b/src/deepcode/constants/views.ts @@ -8,6 +8,7 @@ export const DEEPCODE_CONTEXT = { APPROVED: "uploadApproved", ANALYZING: "workspaceFound", ERROR: "error", + MODE: "mode", }; export const DEEPCODE_ERROR_CODES = { @@ -15,6 +16,13 @@ export const DEEPCODE_ERROR_CODES = { BLOCKING: "blocking", }; +export const DEEPCODE_MODE_CODES = { + AUTO: 'auto', + MANUAL: 'manual', + PAUSED: 'paused', + THROTTLED: 'throttled', +}; + export const DEEPCODE_ANALYSIS_STATUS = { COLLECTING: "Collecting files", HASHING: "Hashing files", diff --git a/src/deepcode/lib/modules/DeepCodeLib.ts b/src/deepcode/lib/modules/DeepCodeLib.ts index 3043b14c..12b0cf5b 100644 --- a/src/deepcode/lib/modules/DeepCodeLib.ts +++ b/src/deepcode/lib/modules/DeepCodeLib.ts @@ -2,11 +2,37 @@ import * as _ from "lodash"; import DeepCode from "../../../interfaces/DeepCodeInterfaces"; import BundlesModule from "./BundlesModule"; import { setContext } from "../../utils/vscodeCommandsUtils"; -import { DEEPCODE_CONTEXT } from "../../constants/views"; -import { EXECUTION_DEBOUNCE_INTERVAL } from "../../constants/general"; +import { DEEPCODE_CONTEXT, DEEPCODE_MODE_CODES } from "../../constants/views"; import { errorsLogs } from "../../messages/errorsServerLogMessages"; +import { + EXECUTION_DEBOUNCE_INTERVAL, + EXECUTION_THROTTLING_INTERVAL, + EXECUTION_PAUSE_INTERVAL, +} from "../../constants/general"; export default class DeepCodeLib extends BundlesModule implements DeepCode.DeepCodeLibInterface { + private _mode = DEEPCODE_MODE_CODES.AUTO; + // Platform-independant type definition. + private _unpauseTimeout: ReturnType | undefined; + private _lastThrottledExecution: number | undefined; + + private shouldBeThrottled(): boolean { + if (this._mode !== DEEPCODE_MODE_CODES.THROTTLED) return false; + const now = Date.now(); + if ( + this._lastThrottledExecution === undefined || + (now - this._lastThrottledExecution) >= EXECUTION_THROTTLING_INTERVAL + ) { + this._lastThrottledExecution = now; + return false; + } + return true; + } + + private unpause(): void { + if (this._mode === DEEPCODE_MODE_CODES.PAUSED) this.setMode(DEEPCODE_MODE_CODES.AUTO); + } + activateAll(): void { // this.filesWatcher.activate(this); this.workspacesWatcher.activate(this); @@ -28,10 +54,19 @@ export default class DeepCodeLib extends BundlesModule implements DeepCode.DeepC this.resetTransientErrors(); } + // This function is called by commands, error handlers, etc. + // We should avoid having duplicate parallel executions. startExtension = _.debounce( - async (): Promise => { - // This function is called by commands, error handlers, etc. - // We should avoid having duplicate parallel executions. + async (manual = false): Promise => { + // If the execution is suspended, we only allow user-triggered analyses. + if (!manual) { + if ([ + DEEPCODE_MODE_CODES.MANUAL, + DEEPCODE_MODE_CODES.PAUSED + ].includes(this._mode) || + this.shouldBeThrottled() + ) return; + } try { await this.executeExtensionPipeline(); } catch (err) { @@ -43,4 +78,20 @@ export default class DeepCodeLib extends BundlesModule implements DeepCode.DeepC EXECUTION_DEBOUNCE_INTERVAL, { 'leading': true } ); + + setMode(mode: string): void { + if (!Object.values(DEEPCODE_MODE_CODES).includes(mode)) return; + this._mode = mode; + setContext(DEEPCODE_CONTEXT.MODE, mode); + switch(mode) { + case DEEPCODE_MODE_CODES.PAUSED: + this._unpauseTimeout = setTimeout(this.unpause.bind(this), EXECUTION_PAUSE_INTERVAL); + break; + case DEEPCODE_MODE_CODES.AUTO: + case DEEPCODE_MODE_CODES.MANUAL: + case DEEPCODE_MODE_CODES.THROTTLED: + if (this._unpauseTimeout) clearTimeout(this._unpauseTimeout); + break; + } + } } diff --git a/src/interfaces/DeepCodeInterfaces.ts b/src/interfaces/DeepCodeInterfaces.ts index 0beee754..564a8438 100644 --- a/src/interfaces/DeepCodeInterfaces.ts +++ b/src/interfaces/DeepCodeInterfaces.ts @@ -234,6 +234,7 @@ namespace DeepCode { export interface DeepCodeLibInterface { activateAll(): void; + setMode(mode:string): void; } export interface ExtensionInterface