Skip to content

Commit c245dee

Browse files
committed
Adds experimental code generation
This adds a 'mail merge' hacky way of doing the code generation without a tool. Experimental only via config option
1 parent 4e97d18 commit c245dee

File tree

3 files changed

+143
-7
lines changed

3 files changed

+143
-7
lines changed

package.json

+5
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,11 @@
9292
"type": "boolean",
9393
"default": false,
9494
"description": "Enable verbose logging"
95+
},
96+
"resx-editor.generateCode": {
97+
"type": "boolean",
98+
"default": false,
99+
"description": "Generate .Designer.cs files when modifying RESX files (Experimental)"
95100
}
96101
}
97102
},

src/resxProvider.ts

+51-7
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,11 @@
11
import * as vscode from 'vscode';
22
import * as resx from 'resx';
3+
import * as path from 'path';
34
import { getNonce } from './utilities/getNonce';
45
import { printChannelOutput } from './extension';
56
import { newResourceInput } from './addNewResource';
67
import { AppConstants } from './utilities/constants';
8+
import { generateDesignerCode } from './utilities/designerGenerator';
79

810
export class ResxProvider implements vscode.CustomTextEditorProvider {
911

@@ -120,14 +122,56 @@ export class ResxProvider implements vscode.CustomTextEditorProvider {
120122
}
121123

122124
private async updateTextDocument(document: vscode.TextDocument, json: any) {
125+
try {
126+
const parsedJson = JSON.parse(json);
127+
const edit = new vscode.WorkspaceEdit();
128+
129+
// Update the RESX file
130+
const resxContent = await resx.js2resx(parsedJson);
131+
edit.replace(
132+
document.uri,
133+
new vscode.Range(0, 0, document.lineCount, 0),
134+
resxContent
135+
);
136+
137+
// Check if code generation is enabled
138+
const config = vscode.workspace.getConfiguration('resx-editor');
139+
const generateCode = config.get<boolean>('generateCode', true);
140+
141+
if (generateCode) {
142+
// Generate and update the Designer.cs file
143+
const designerPath = document.uri.fsPath.replace('.resx', '.Designer.cs');
144+
const designerUri = vscode.Uri.file(designerPath);
145+
const designerCode = generateDesignerCode(document.uri.fsPath, parsedJson);
146+
147+
try {
148+
await vscode.workspace.fs.stat(designerUri);
149+
// File exists, write contents directly
150+
printChannelOutput(`Updating existing Designer file at ${designerPath}`, true);
151+
await vscode.workspace.fs.writeFile(designerUri, Buffer.from(designerCode, 'utf8'));
152+
} catch {
153+
// File doesn't exist, create it
154+
printChannelOutput(`Creating new Designer file at ${designerPath}`, true);
155+
await vscode.workspace.fs.writeFile(designerUri, Buffer.from(designerCode, 'utf8'));
156+
}
157+
} else {
158+
printChannelOutput('Code generation is disabled, skipping Designer.cs file update', true);
159+
}
123160

124-
const edit = new vscode.WorkspaceEdit();
125-
126-
edit.replace(
127-
document.uri,
128-
new vscode.Range(0, 0, document.lineCount, 0),
129-
await resx.js2resx(JSON.parse(json)));
130-
return vscode.workspace.applyEdit(edit);
161+
const success = await vscode.workspace.applyEdit(edit);
162+
if (success) {
163+
printChannelOutput(`Successfully updated RESX${generateCode ? ' and Designer' : ''} files`, true);
164+
} else {
165+
printChannelOutput(`Failed to apply workspace edits`, true);
166+
vscode.window.showErrorMessage('Failed to update resource files');
167+
}
168+
return success;
169+
} catch (error) {
170+
const errorMessage = `Error updating resource files: ${error instanceof Error ? error.message : String(error)}`;
171+
printChannelOutput(errorMessage, true);
172+
vscode.window.showErrorMessage(errorMessage);
173+
return false;
174+
}
131175
}
132176

133177
private _getWebviewContent(webview: vscode.Webview) {

src/utilities/designerGenerator.ts

+87
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
import * as path from 'path';
2+
3+
export function generateDesignerCode(resxPath: string, resources: { [key: string]: { value: string; comment?: string } }): string {
4+
const fileName = path.basename(resxPath, '.resx');
5+
const namespaceName = fileName.includes('.') ? fileName.split('.')[0] : 'Resources';
6+
const className = fileName.includes('.') ? fileName.split('.')[1] : fileName;
7+
8+
let code = `//------------------------------------------------------------------------------
9+
// <auto-generated>
10+
// This code was generated by a tool.
11+
// Runtime Version:4.0.30319.42000
12+
//
13+
// Changes to this file may cause incorrect behavior and will be lost if
14+
// the code is regenerated.
15+
// </auto-generated>
16+
//------------------------------------------------------------------------------
17+
18+
namespace ${namespaceName} {
19+
using System;
20+
21+
/// <summary>
22+
/// A strongly-typed resource class, for looking up localized strings, etc.
23+
/// </summary>
24+
[global::System.CodeDom.Compiler.GeneratedCodeAttribute("VS Code RESX Editor", "1.0.0.0")]
25+
[global::System.Diagnostics.DebuggerNonUserCodeAttribute()]
26+
[global::System.Runtime.CompilerServices.CompilerGeneratedAttribute()]
27+
public class ${className} {
28+
29+
private static global::System.Resources.ResourceManager resourceMan;
30+
31+
private static global::System.Globalization.CultureInfo resourceCulture;
32+
33+
[global::System.Diagnostics.CodeAnalysis.SuppressMessageAttribute("Microsoft.Performance", "CA1811:AvoidUncalledPrivateCode")]
34+
internal ${className}() {
35+
}
36+
37+
/// <summary>
38+
/// Returns the cached ResourceManager instance used by this class.
39+
/// </summary>
40+
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
41+
public static global::System.Resources.ResourceManager ResourceManager {
42+
get {
43+
if (object.ReferenceEquals(resourceMan, null)) {
44+
global::System.Resources.ResourceManager temp = new global::System.Resources.ResourceManager("${namespaceName}.${className}", typeof(${className}).Assembly);
45+
resourceMan = temp;
46+
}
47+
return resourceMan;
48+
}
49+
}
50+
51+
/// <summary>
52+
/// Overrides the current thread's CurrentUICulture property for all
53+
/// resource lookups using this strongly typed resource class.
54+
/// </summary>
55+
[global::System.ComponentModel.EditorBrowsableAttribute(global::System.ComponentModel.EditorBrowsableState.Advanced)]
56+
public static global::System.Globalization.CultureInfo Culture {
57+
get {
58+
return resourceCulture;
59+
}
60+
set {
61+
resourceCulture = value;
62+
}
63+
}
64+
65+
`;
66+
67+
// Generate a property for each resource
68+
for (const [key, resource] of Object.entries(resources)) {
69+
if (resource.comment) {
70+
code += ` /// <summary>\n`;
71+
code += ` /// ${resource.comment}\n`;
72+
code += ` /// </summary>\n`;
73+
}
74+
code += ` public static string ${key} {
75+
get {
76+
return ResourceManager.GetString("${key}", resourceCulture);
77+
}
78+
}
79+
80+
`;
81+
}
82+
83+
code += ` }
84+
}`;
85+
86+
return code;
87+
}

0 commit comments

Comments
 (0)