Skip to content

Commit

Permalink
feat: new command to copy key values from existing ones
Browse files Browse the repository at this point in the history
  • Loading branch information
hsayed21 committed Dec 27, 2024
1 parent 311904b commit badf884
Show file tree
Hide file tree
Showing 6 changed files with 150 additions and 2 deletions.
2 changes: 2 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
- **Suggest Existing Keys for Reuse**: When adding a new translation key, the extension will suggest existing keys for reuse to avoid redundancy.
- **Cleanup Unused Translation Keys**: Detect and clean up unused translation keys automatically.
- **Diagnostics and Quick Fixes**: Automatically detect missing translation keys in your code and provide quick fixes to add them.
- **Copy Existing Translation Key**: Copy values from existing translation keys to new or updated keys, ensuring consistency and saving time.

## How to Use

Expand Down Expand Up @@ -80,6 +81,7 @@
- [x] Suggest existing keys for reuse when adding new ones.
- [ ] Rename keys and update references in code automatically.
- [x] Detect and clean up unused translation keys automatically.
- [x] Copy values from existing translation keys to new or updated keys.

## Contributing

Expand Down
5 changes: 5 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -57,6 +57,11 @@
"command": "json-i18n-key.cleanupUnusedKeys",
"title": "Cleanup Unused Translation Keys",
"category": "Json I18n Key"
},
{
"command": "json-i18n-key.copyKey",
"title": "Copy Translation Key",
"category": "Json I18n Key"
}
],
"configuration": {
Expand Down
113 changes: 113 additions & 0 deletions src/commands/copyKey.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,113 @@
import * as vscode from "vscode";
import { JsonI18nKeySettings } from "../models/JsonI18nKeySettings";
import { flattenKeysWithValues, getKeyValuesFromAllFiles, loadKeys } from "../utils/jsonUtils";
import { convertCase } from "../utils/globalUtils";
import { autoDetectI18nFiles } from "../options/auto-detect-i18n-files";
import { KEY_PATH_REGEX } from "../utils/constants";
import { JsonParser } from "../utils/json-parser";
import { loadJsonFileSync } from "../utils/fileUtils";

export async function copyKeyCommand() {
const editor = vscode.window.activeTextEditor;
if (!editor) {
return; // No open text editor
}

await autoDetectI18nFiles();

let keyPath: string | undefined = undefined;
let originalKeyPath: string | undefined = undefined;
const settings = JsonI18nKeySettings.instance;
if (settings.translationFiles.length === 0) {
vscode.window.showErrorMessage("No translation files found");
return;
}

if (settings.typeOfGetKey === "Manual") {
keyPath = await vscode.window.showInputBox({ prompt: "Enter Key Path:" });
} else if (settings.typeOfGetKey === "Clipboard") {
const clipboard = await vscode.env.clipboard.readText();
keyPath = clipboard;
} else {
const position = editor.selection.active;
// Match only keypath without spaces
const range = editor.document.getWordRangeAtPosition(position, KEY_PATH_REGEX);
if (range) {
keyPath = editor.document.getText(range);
// Clean up quotes
keyPath = keyPath.replace(/^['"`]|['"`]$/g, "");

if (keyPath.includes(" ")) {
vscode.window.showErrorMessage("Key path cannot contain spaces");
return;
}
} else {
vscode.window.showErrorMessage("Can't get key path by regex");
return;
}
}

if (keyPath === undefined) return;

if (!keyPath || keyPath.includes(" ")) {
vscode.window.showErrorMessage("Key path is required and cannot contain spaces");
return;
}

originalKeyPath = keyPath;

const jsonData = flattenKeysWithValues(
loadJsonFileSync(JsonI18nKeySettings.instance.enJsonFilePath)
);

keyPath = await new Promise<string>((resolve) => {
const quickPick = vscode.window.createQuickPick();
quickPick.matchOnDescription = true;
quickPick.placeholder = "Select an existing key to copy values or none to continue";

const allItems = jsonData.map(({ key, value }) => ({
label: key,
description: String(value),
}));

quickPick.items = [
{ label: "$(close) None", description: "Continue without selecting existing key" },
...allItems,
];

quickPick.onDidAccept(() => {
let selectedKey = "";
if (quickPick.selectedItems.length > 0) {
const selected = quickPick.selectedItems[0];
selectedKey = selected.label;
}
if (selectedKey === "$(close) None") {
selectedKey = "";
} else {
selectedKey = selectedKey || quickPick.value;
}

quickPick.hide();
resolve(selectedKey ?? "");
});

quickPick.onDidHide(() => {
quickPick.dispose();
});

quickPick.show();
});

if (!keyPath) {
return;
}

const keyValues = getKeyValuesFromAllFiles(keyPath);
for (const value of keyValues) {
new JsonParser(value.filePath, settings.preserveFormating).addKey(
originalKeyPath,
value.value,
true
);
}
}
2 changes: 2 additions & 0 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ import { cleanupUnusedKeys } from './commands/cleanupUnusedKeys';
import { KEY_PATH_REGEX, SUPPORTED_LANGUAGES } from './utils/constants';
import { DiagnosticManager } from './utils/diagnosticManager';
import path from 'path';
import { copyKeyCommand } from './commands/copyKey';

let outputChannel: vscode.OutputChannel | undefined;
let keyCache: Set<string> = new Set();
Expand Down Expand Up @@ -114,6 +115,7 @@ export function activate(context: vscode.ExtensionContext): void {
vscode.commands.registerCommand('json-i18n-key.addKey', addKeyCommand),
vscode.commands.registerCommand('json-i18n-key.searchKey', searchKeyCommand),
vscode.commands.registerCommand('json-i18n-key.cleanupUnusedKeys', cleanupUnusedKeys),
vscode.commands.registerCommand('json-i18n-key.copyKey', copyKeyCommand),
hoverProvider,
completionProvider,
watcher!,
Expand Down
4 changes: 2 additions & 2 deletions src/utils/json-parser.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,11 +107,11 @@ export class JsonParser {
vscode.window.showInformationMessage(`${key}: Key updated in '${this.fileName}'`);
}

addKey(keyPath: string, value: string) {
addKey(keyPath: string, value: string, overrideForCopy: boolean = false) {
const jsonData = loadJsonFileSync(this.jsonFilePath, this.preserveFormatting);
const key = keyPath.split('.').pop() as string;

if (!JsonI18nKeySettings.instance.overrideKeyIfExistsWhenAdding && checkExistKey(this.jsonFilePath, keyPath)) {
if (!JsonI18nKeySettings.instance.overrideKeyIfExistsWhenAdding && checkExistKey(this.jsonFilePath, keyPath) && !overrideForCopy) {
vscode.window.showErrorMessage(`${key}: Key already exists in ${this.fileName}`);
return;
}
Expand Down
26 changes: 26 additions & 0 deletions src/utils/jsonUtils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@ import * as vscode from 'vscode';
import { loadJsonFileSync } from './fileUtils';
import { JSONPath } from 'jsonpath-plus';
import { printChannelOutput } from '../extension';
import * as fs from 'fs';
import * as path from 'path';

export function checkExistKey(jsonFilePath: string, keyPath: string): boolean {
const jsonData = loadJsonFileSync(jsonFilePath, false);
Expand Down Expand Up @@ -34,6 +36,30 @@ export function getKeysValues(keyPath: string): string[] {
return result;
}


export function getKeyValuesFromAllFiles(keyPath: string): { filePath: string, lang: string, value: string }[] {
const results: {filePath: string, lang: string, value: string }[] = [];

JsonI18nKeySettings.instance.translationFiles.forEach(file => {
const jsonData = loadJsonFileSync(file.filePath);
if (jsonData !== null) {
const value = JSONPath({
json: jsonData,
path: keyPath
});

results.push({
filePath: file.filePath,
lang: file.lang,
value: value.length > 0 ? value[0] : ''
});
}
});

return results;
}


export function GetAlli18nFilesKeys(key: string | null = null): string[] {
const uniqueKeys = new Set<string>();
JsonI18nKeySettings.instance.translationFiles.forEach(file => {
Expand Down

0 comments on commit badf884

Please sign in to comment.