mirror of
https://github.com/microsoft/vscode-extension-samples.git
synced 2026-04-27 16:55:44 +08:00
Add user-profile-sample with three variations
Co-authored-by: pierceboggan <1091304+pierceboggan@users.noreply.github.com>
This commit is contained in:
4
user-profile-sample/.gitignore
vendored
Normal file
4
user-profile-sample/.gitignore
vendored
Normal file
@ -0,0 +1,4 @@
|
||||
out
|
||||
node_modules
|
||||
.vscode-test
|
||||
*.vsix
|
||||
17
user-profile-sample/.vscode/launch.json
vendored
Normal file
17
user-profile-sample/.vscode/launch.json
vendored
Normal file
@ -0,0 +1,17 @@
|
||||
{
|
||||
"version": "0.2.0",
|
||||
"configurations": [
|
||||
{
|
||||
"name": "Run Extension",
|
||||
"type": "extensionHost",
|
||||
"request": "launch",
|
||||
"args": [
|
||||
"--extensionDevelopmentPath=${workspaceFolder}"
|
||||
],
|
||||
"outFiles": [
|
||||
"${workspaceFolder}/out/**/*.js"
|
||||
],
|
||||
"preLaunchTask": "${defaultBuildTask}"
|
||||
}
|
||||
]
|
||||
}
|
||||
18
user-profile-sample/.vscode/tasks.json
vendored
Normal file
18
user-profile-sample/.vscode/tasks.json
vendored
Normal file
@ -0,0 +1,18 @@
|
||||
{
|
||||
"version": "2.0.0",
|
||||
"tasks": [
|
||||
{
|
||||
"type": "npm",
|
||||
"script": "watch",
|
||||
"problemMatcher": "$tsc-watch",
|
||||
"isBackground": true,
|
||||
"presentation": {
|
||||
"reveal": "never"
|
||||
},
|
||||
"group": {
|
||||
"kind": "build",
|
||||
"isDefault": true
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
8
user-profile-sample/.vscodeignore
Normal file
8
user-profile-sample/.vscodeignore
Normal file
@ -0,0 +1,8 @@
|
||||
.vscode/**
|
||||
.vscode-test/**
|
||||
src/**
|
||||
.gitignore
|
||||
.eslintrc.json
|
||||
eslint.config.mjs
|
||||
**/*.map
|
||||
**/*.ts
|
||||
100
user-profile-sample/README.md
Normal file
100
user-profile-sample/README.md
Normal file
@ -0,0 +1,100 @@
|
||||
# User Profile Sample - Three Variations
|
||||
|
||||
This sample demonstrates **three different variations** of implementing user profile management in a VS Code extension. Each variation shows a different approach to storing and retrieving user data.
|
||||
|
||||
## The Three Variations
|
||||
|
||||
### Variation 1: In-Memory Storage
|
||||
**Simplest approach** - Data exists only during the extension's lifetime.
|
||||
|
||||
**Characteristics:**
|
||||
- ✅ Fast and simple
|
||||
- ❌ Data is lost when VS Code restarts
|
||||
- ❌ Not suitable for persistent data
|
||||
|
||||
**Use case:** Temporary session data, cache
|
||||
|
||||
**Commands:**
|
||||
- `User Profile: Set Profile (Variation 1 - In-Memory)` - Set user profile
|
||||
- `User Profile: Show Profile (Variation 1 - In-Memory)` - Display current profile
|
||||
|
||||
### Variation 2: Workspace State Storage
|
||||
**Workspace-specific persistence** - Data persists per workspace.
|
||||
|
||||
**Characteristics:**
|
||||
- ✅ Survives VS Code restarts
|
||||
- ✅ Different workspaces can have different profiles
|
||||
- ❌ Data is not shared across workspaces
|
||||
|
||||
**Use case:** Project-specific user preferences, workspace-specific settings
|
||||
|
||||
**Commands:**
|
||||
- `User Profile: Set Profile (Variation 2 - Workspace State)` - Set workspace profile
|
||||
- `User Profile: Show Profile (Variation 2 - Workspace State)` - Display workspace profile
|
||||
|
||||
### Variation 3: Global State with Settings Integration
|
||||
**Global persistence with defaults** - Data persists globally across all workspaces.
|
||||
|
||||
**Characteristics:**
|
||||
- ✅ Survives VS Code restarts
|
||||
- ✅ Shared across all workspaces
|
||||
- ✅ Integrates with VS Code settings for default values
|
||||
- ✅ Most feature-rich
|
||||
|
||||
**Use case:** User identity, global preferences, account information
|
||||
|
||||
**Commands:**
|
||||
- `User Profile: Set Profile (Variation 3 - Global State)` - Set global profile
|
||||
- `User Profile: Show Profile (Variation 3 - Global State)` - Display global profile
|
||||
|
||||
**Settings:**
|
||||
- `userProfile.defaultName` - Default user name
|
||||
- `userProfile.defaultEmail` - Default user email
|
||||
|
||||
## How to Run
|
||||
|
||||
1. Run `npm install` in terminal to install dependencies
|
||||
2. Run the `Run Extension` target in the Debug View (or press `F5`)
|
||||
3. In the new VS Code window, open the Command Palette (`Ctrl+Shift+P` or `Cmd+Shift+P`)
|
||||
4. Try any of the six commands to see each variation in action
|
||||
|
||||
## Comparison Table
|
||||
|
||||
| Feature | Variation 1 | Variation 2 | Variation 3 |
|
||||
|---------|-------------|-------------|-------------|
|
||||
| **Storage Type** | In-Memory | Workspace State | Global State |
|
||||
| **Persists on Restart** | ❌ No | ✅ Yes | ✅ Yes |
|
||||
| **Shared Across Workspaces** | N/A | ❌ No | ✅ Yes |
|
||||
| **Settings Integration** | ❌ No | ❌ No | ✅ Yes |
|
||||
| **Complexity** | Low | Medium | High |
|
||||
| **Best For** | Session data | Workspace preferences | User identity |
|
||||
|
||||
## VS Code API Used
|
||||
|
||||
### `vscode` module
|
||||
- [`ExtensionContext.workspaceState`](https://code.visualstudio.com/api/references/vscode-api#ExtensionContext.workspaceState) - Workspace-specific storage
|
||||
- [`ExtensionContext.globalState`](https://code.visualstudio.com/api/references/vscode-api#ExtensionContext.globalState) - Global storage
|
||||
- [`Memento`](https://code.visualstudio.com/api/references/vscode-api#Memento) - Key-value storage interface
|
||||
- [`workspace.getConfiguration`](https://code.visualstudio.com/api/references/vscode-api#workspace.getConfiguration) - Access extension settings
|
||||
- [`window.showInputBox`](https://code.visualstudio.com/api/references/vscode-api#window.showInputBox) - User input
|
||||
- [`window.showInformationMessage`](https://code.visualstudio.com/api/references/vscode-api#window.showInformationMessage) - Display messages
|
||||
|
||||
### Contribution Points
|
||||
- [`contributes.commands`](https://code.visualstudio.com/api/references/contribution-points#contributes.commands) - Register commands
|
||||
- [`contributes.configuration`](https://code.visualstudio.com/api/references/contribution-points#contributes.configuration) - Define settings
|
||||
|
||||
## Key Learning Points
|
||||
|
||||
1. **In-Memory vs Persistent Storage**: Understand when to use temporary vs persistent data storage
|
||||
2. **Workspace vs Global State**: Learn the difference between workspace-specific and global data
|
||||
3. **Settings Integration**: See how to provide default values through VS Code settings
|
||||
4. **Memento API**: Master the key-value storage interface for VS Code extensions
|
||||
|
||||
## Implementation Details
|
||||
|
||||
Each variation is implemented as a separate class:
|
||||
- `InMemoryProfileManager` - Simple in-memory storage
|
||||
- `WorkspaceProfileManager` - Uses `workspaceState` Memento
|
||||
- `GlobalProfileManager` - Uses `globalState` Memento with settings fallback
|
||||
|
||||
All three variations implement the same interface for setting and getting user profiles, making it easy to compare their different storage mechanisms.
|
||||
44
user-profile-sample/eslint.config.mjs
Normal file
44
user-profile-sample/eslint.config.mjs
Normal file
@ -0,0 +1,44 @@
|
||||
/**
|
||||
* ESLint configuration for the project.
|
||||
*
|
||||
* See https://eslint.style and https://typescript-eslint.io for additional linting options.
|
||||
*/
|
||||
// @ts-check
|
||||
import js from '@eslint/js';
|
||||
import tseslint from 'typescript-eslint';
|
||||
import stylistic from '@stylistic/eslint-plugin';
|
||||
|
||||
export default tseslint.config(
|
||||
{
|
||||
ignores: [
|
||||
'.vscode-test',
|
||||
'out',
|
||||
]
|
||||
},
|
||||
js.configs.recommended,
|
||||
...tseslint.configs.recommended,
|
||||
...tseslint.configs.stylistic,
|
||||
{
|
||||
plugins: {
|
||||
'@stylistic': stylistic
|
||||
},
|
||||
rules: {
|
||||
'curly': 'warn',
|
||||
'@stylistic/semi': ['warn', 'always'],
|
||||
'@typescript-eslint/no-empty-function': 'off',
|
||||
'@typescript-eslint/naming-convention': [
|
||||
'warn',
|
||||
{
|
||||
'selector': 'import',
|
||||
'format': ['camelCase', 'PascalCase']
|
||||
}
|
||||
],
|
||||
'@typescript-eslint/no-unused-vars': [
|
||||
'error',
|
||||
{
|
||||
'argsIgnorePattern': '^_'
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
);
|
||||
1737
user-profile-sample/package-lock.json
generated
Normal file
1737
user-profile-sample/package-lock.json
generated
Normal file
File diff suppressed because it is too large
Load Diff
74
user-profile-sample/package.json
Normal file
74
user-profile-sample/package.json
Normal file
@ -0,0 +1,74 @@
|
||||
{
|
||||
"name": "user-profile-sample",
|
||||
"displayName": "User Profile Sample",
|
||||
"description": "Sample showing three variations of user profile management",
|
||||
"version": "0.0.1",
|
||||
"publisher": "vscode-samples",
|
||||
"repository": "https://github.com/Microsoft/vscode-extension-samples/user-profile-sample",
|
||||
"engines": {
|
||||
"vscode": "^1.100.0"
|
||||
},
|
||||
"categories": [
|
||||
"Other"
|
||||
],
|
||||
"activationEvents": [],
|
||||
"main": "./out/extension.js",
|
||||
"contributes": {
|
||||
"commands": [
|
||||
{
|
||||
"command": "userProfile.variation1.set",
|
||||
"title": "User Profile: Set Profile (Variation 1 - In-Memory)"
|
||||
},
|
||||
{
|
||||
"command": "userProfile.variation1.show",
|
||||
"title": "User Profile: Show Profile (Variation 1 - In-Memory)"
|
||||
},
|
||||
{
|
||||
"command": "userProfile.variation2.set",
|
||||
"title": "User Profile: Set Profile (Variation 2 - Workspace State)"
|
||||
},
|
||||
{
|
||||
"command": "userProfile.variation2.show",
|
||||
"title": "User Profile: Show Profile (Variation 2 - Workspace State)"
|
||||
},
|
||||
{
|
||||
"command": "userProfile.variation3.set",
|
||||
"title": "User Profile: Set Profile (Variation 3 - Global State)"
|
||||
},
|
||||
{
|
||||
"command": "userProfile.variation3.show",
|
||||
"title": "User Profile: Show Profile (Variation 3 - Global State)"
|
||||
}
|
||||
],
|
||||
"configuration": {
|
||||
"title": "User Profile",
|
||||
"properties": {
|
||||
"userProfile.defaultName": {
|
||||
"type": "string",
|
||||
"default": "Guest",
|
||||
"description": "Default user name for Variation 3"
|
||||
},
|
||||
"userProfile.defaultEmail": {
|
||||
"type": "string",
|
||||
"default": "guest@example.com",
|
||||
"description": "Default user email for Variation 3"
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
"scripts": {
|
||||
"vscode:prepublish": "npm run compile",
|
||||
"compile": "tsc -p ./",
|
||||
"lint": "eslint",
|
||||
"watch": "tsc -watch -p ./"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@eslint/js": "^9.13.0",
|
||||
"@stylistic/eslint-plugin": "^2.9.0",
|
||||
"@types/node": "^22",
|
||||
"@types/vscode": "^1.100.0",
|
||||
"eslint": "^9.13.0",
|
||||
"typescript": "^5.8.2",
|
||||
"typescript-eslint": "^8.26.0"
|
||||
}
|
||||
}
|
||||
218
user-profile-sample/src/extension.ts
Normal file
218
user-profile-sample/src/extension.ts
Normal file
@ -0,0 +1,218 @@
|
||||
import * as vscode from 'vscode';
|
||||
|
||||
// Interface for user profile
|
||||
interface UserProfile {
|
||||
name: string;
|
||||
email: string;
|
||||
role?: string;
|
||||
}
|
||||
|
||||
/**
|
||||
* VARIATION 1: In-Memory Storage
|
||||
* Simplest approach - data exists only during the extension's lifetime
|
||||
* Data is lost when VS Code is restarted
|
||||
*/
|
||||
class InMemoryProfileManager {
|
||||
private profile: UserProfile | null = null;
|
||||
|
||||
setProfile(profile: UserProfile): void {
|
||||
this.profile = profile;
|
||||
}
|
||||
|
||||
getProfile(): UserProfile | null {
|
||||
return this.profile;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* VARIATION 2: Workspace State Storage
|
||||
* Data persists per workspace
|
||||
* Different workspaces can have different profiles
|
||||
*/
|
||||
class WorkspaceProfileManager {
|
||||
private static readonly PROFILE_KEY = 'userProfile';
|
||||
|
||||
constructor(private workspaceState: vscode.Memento) {}
|
||||
|
||||
async setProfile(profile: UserProfile): Promise<void> {
|
||||
await this.workspaceState.update(WorkspaceProfileManager.PROFILE_KEY, profile);
|
||||
}
|
||||
|
||||
getProfile(): UserProfile | null {
|
||||
return this.workspaceState.get<UserProfile>(WorkspaceProfileManager.PROFILE_KEY) || null;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* VARIATION 3: Global State Storage with Settings Integration
|
||||
* Data persists globally across all workspaces
|
||||
* Integrates with VS Code settings for default values
|
||||
*/
|
||||
class GlobalProfileManager {
|
||||
private static readonly PROFILE_KEY = 'userProfile';
|
||||
|
||||
constructor(private globalState: vscode.Memento) {}
|
||||
|
||||
async setProfile(profile: UserProfile): Promise<void> {
|
||||
await this.globalState.update(GlobalProfileManager.PROFILE_KEY, profile);
|
||||
}
|
||||
|
||||
getProfile(): UserProfile | null {
|
||||
const profile = this.globalState.get<UserProfile>(GlobalProfileManager.PROFILE_KEY);
|
||||
|
||||
if (profile) {
|
||||
return profile;
|
||||
}
|
||||
|
||||
// Fallback to default values from settings
|
||||
const config = vscode.workspace.getConfiguration('userProfile');
|
||||
const defaultName = config.get<string>('defaultName', 'Guest');
|
||||
const defaultEmail = config.get<string>('defaultEmail', 'guest@example.com');
|
||||
|
||||
return {
|
||||
name: defaultName,
|
||||
email: defaultEmail,
|
||||
role: 'Default User'
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
export function activate(context: vscode.ExtensionContext) {
|
||||
console.log('User Profile Sample extension is now active!');
|
||||
|
||||
// Initialize all three variations
|
||||
const inMemoryManager = new InMemoryProfileManager();
|
||||
const workspaceManager = new WorkspaceProfileManager(context.workspaceState);
|
||||
const globalManager = new GlobalProfileManager(context.globalState);
|
||||
|
||||
// VARIATION 1 Commands: In-Memory
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('userProfile.variation1.set', async () => {
|
||||
const name = await vscode.window.showInputBox({
|
||||
prompt: 'Enter your name',
|
||||
placeHolder: 'John Doe'
|
||||
});
|
||||
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const email = await vscode.window.showInputBox({
|
||||
prompt: 'Enter your email',
|
||||
placeHolder: 'john.doe@example.com'
|
||||
});
|
||||
|
||||
if (!email) {
|
||||
return;
|
||||
}
|
||||
|
||||
inMemoryManager.setProfile({ name, email });
|
||||
vscode.window.showInformationMessage(
|
||||
`Profile saved (In-Memory). Note: This will be lost when VS Code restarts.`
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('userProfile.variation1.show', () => {
|
||||
const profile = inMemoryManager.getProfile();
|
||||
if (profile) {
|
||||
vscode.window.showInformationMessage(
|
||||
`In-Memory Profile: ${profile.name} (${profile.email})`
|
||||
);
|
||||
} else {
|
||||
vscode.window.showWarningMessage('No profile set for Variation 1');
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// VARIATION 2 Commands: Workspace State
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('userProfile.variation2.set', async () => {
|
||||
const name = await vscode.window.showInputBox({
|
||||
prompt: 'Enter your name',
|
||||
placeHolder: 'John Doe'
|
||||
});
|
||||
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const email = await vscode.window.showInputBox({
|
||||
prompt: 'Enter your email',
|
||||
placeHolder: 'john.doe@example.com'
|
||||
});
|
||||
|
||||
if (!email) {
|
||||
return;
|
||||
}
|
||||
|
||||
await workspaceManager.setProfile({ name, email });
|
||||
vscode.window.showInformationMessage(
|
||||
`Profile saved (Workspace State). This persists per workspace.`
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('userProfile.variation2.show', () => {
|
||||
const profile = workspaceManager.getProfile();
|
||||
if (profile) {
|
||||
vscode.window.showInformationMessage(
|
||||
`Workspace Profile: ${profile.name} (${profile.email})`
|
||||
);
|
||||
} else {
|
||||
vscode.window.showWarningMessage('No profile set for Variation 2');
|
||||
}
|
||||
})
|
||||
);
|
||||
|
||||
// VARIATION 3 Commands: Global State with Settings
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('userProfile.variation3.set', async () => {
|
||||
const name = await vscode.window.showInputBox({
|
||||
prompt: 'Enter your name',
|
||||
placeHolder: 'John Doe'
|
||||
});
|
||||
|
||||
if (!name) {
|
||||
return;
|
||||
}
|
||||
|
||||
const email = await vscode.window.showInputBox({
|
||||
prompt: 'Enter your email',
|
||||
placeHolder: 'john.doe@example.com'
|
||||
});
|
||||
|
||||
if (!email) {
|
||||
return;
|
||||
}
|
||||
|
||||
const role = await vscode.window.showInputBox({
|
||||
prompt: 'Enter your role (optional)',
|
||||
placeHolder: 'Developer'
|
||||
});
|
||||
|
||||
await globalManager.setProfile({ name, email, role: role || 'User' });
|
||||
vscode.window.showInformationMessage(
|
||||
`Profile saved (Global State). This persists across all workspaces.`
|
||||
);
|
||||
})
|
||||
);
|
||||
|
||||
context.subscriptions.push(
|
||||
vscode.commands.registerCommand('userProfile.variation3.show', () => {
|
||||
const profile = globalManager.getProfile();
|
||||
if (profile) {
|
||||
const roleText = profile.role ? ` - ${profile.role}` : '';
|
||||
vscode.window.showInformationMessage(
|
||||
`Global Profile: ${profile.name} (${profile.email})${roleText}`
|
||||
);
|
||||
} else {
|
||||
vscode.window.showWarningMessage('No profile set for Variation 3');
|
||||
}
|
||||
})
|
||||
);
|
||||
}
|
||||
|
||||
export function deactivate() {}
|
||||
13
user-profile-sample/tsconfig.json
Normal file
13
user-profile-sample/tsconfig.json
Normal file
@ -0,0 +1,13 @@
|
||||
{
|
||||
"compilerOptions": {
|
||||
"module": "Node16",
|
||||
"target": "ES2022",
|
||||
"lib": ["ES2022"],
|
||||
"sourceMap": true,
|
||||
"rootDir": "src",
|
||||
"outDir": "out",
|
||||
"strict": true,
|
||||
"noUnusedLocals": true
|
||||
},
|
||||
"exclude": ["node_modules", ".vscode-test"]
|
||||
}
|
||||
Reference in New Issue
Block a user