blob: 0b97bae36bc2367f4ed73c13f11108a85e7a30aa [file] [log] [blame]
// Copyright 2022 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
import * as vscode from 'vscode';
import * as flags from './all-feature-flags';
/// the current base stability level -- will be autodetected
/// based on version number & config on #init, but can be overriden
/// with the `fuchsia.featurePreview` flag (can change at runtime).
let stabilityLevel: flags.Stability = 'stable';
/// the current default stability, as detected by the extension version
let defaultStability: flags.Stability = 'stable';
let watcher: vscode.Disposable;
export class FeatureFlagChangeEvent {
private global;
constructor(private original: vscode.ConfigurationChangeEvent, global?: boolean) {
this.global = global ?? false;
}
affectsFlag(name: string): boolean {
if (this.global) {
// it'd be complicated to figure out which flags would've flipped, so prob fine to just tell everyone
// that they're affected
return true;
}
return this.original.affectsConfiguration(`fuchsia.features.${name}`);
}
}
const onDidChangeFeatureFlagEmitter: vscode.EventEmitter<FeatureFlagChangeEvent>
= new vscode.EventEmitter();
/**
* Event emitted when a feature flag changes value.
*
* If you need to activate or deactivate features based on flags, listen to this.
*/
export const onDidChangeFeatureFlag: vscode.Event<FeatureFlagChangeEvent>
= onDidChangeFeatureFlagEmitter.event;
/**
* activates the feature flag system, assuming the given extension version.
*
* You can get the extension version with `ctx.extension.packageJSON.version`.
*/
export function activate(extensionVersion: string) {
const [_major, minor, _patch] = extensionVersion.split('.');
const isPrerelease = parseInt(minor) % 2 === 1; // prerelease versions are always odd
defaultStability = isPrerelease ? 'nightly' : 'stable';
setStabilityFromConfig();
watcher = vscode.workspace.onDidChangeConfiguration((evt) => {
if (evt.affectsConfiguration('fuchsia.featurePreview')) {
setStabilityFromConfig();
onDidChangeFeatureFlagEmitter.fire(new FeatureFlagChangeEvent(evt, true));
return;
}
if (evt.affectsConfiguration('fuchsia.features')) {
onDidChangeFeatureFlagEmitter.fire(new FeatureFlagChangeEvent(evt));
}
});
}
/**
* deactives the feature flag system
*/
export function deactivate() {
if (watcher) {
watcher.dispose();
}
}
/**
* resets #stabilityLevel from either the #defaultStability or the override configuration
* (fuchsia.featurePreview)
*/
function setStabilityFromConfig() {
const override = vscode.workspace.getConfiguration('fuchsia').get('featurePreview');
if (override === 'auto') {
stabilityLevel = defaultStability;
} else {
stabilityLevel = override as flags.Stability;
}
}
/** checks if lhs >= rhs, in stability terms */
function isAtLeast(lhs: flags.Stability, rhs: flags.Stability): boolean {
const levels = {
'never': -1,
'nightly': 0,
'stable': 1,
};
return levels[lhs] >= levels[rhs];
}
/**
* Check if the given flag with with the given stability level would be enabled
* in the current environment, either by configuration override or current
* global stability level.
*
* Generally, you don't want to use this outside of generated code. Use the
* functions from feature-flags.ts instead, like
*/
export function enabled(flagName: flags.Known): boolean {
const opt = vscode.workspace.getConfiguration('fuchsia.features').get(flagName);
switch (opt) {
case 'auto':
const stability = flags.features[flagName].stability;
return isAtLeast(stability, stabilityLevel);
case 'enabled':
return true;
case 'disabled':
return false;
default:
throw new Error(
`feature flag ${flagName} set to invalid value "${opt}" (must be enabled|disabled|auto)`);
}
}
// NB(sollyross): these are in a separate file so that we can load it in
// our build generator and inject the corresponding JSON into package.json
/**
* The list of all feature flags.
*/
export { Stability, features } from './all-feature-flags';