diff --git a/src/SharpIDE.Godot/SharpIDE.Godot.csproj b/src/SharpIDE.Godot/SharpIDE.Godot.csproj
index 36bf2b10..c023ca0a 100644
--- a/src/SharpIDE.Godot/SharpIDE.Godot.csproj
+++ b/src/SharpIDE.Godot/SharpIDE.Godot.csproj
@@ -26,7 +26,17 @@
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/src/SharpIDE.Godot/SharpIDE.Godot.sln b/src/SharpIDE.Godot/SharpIDE.Godot.sln
index 5f231ac3..606bd0e1 100644
--- a/src/SharpIDE.Godot/SharpIDE.Godot.sln
+++ b/src/SharpIDE.Godot/SharpIDE.Godot.sln
@@ -19,6 +19,8 @@ Project("{2150E333-8FDC-42A3-9474-1A3956D46DE8}") = "Solution Items", "Solution
..\..\.globalconfig = ..\..\.globalconfig
EndProjectSection
EndProject
+Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "SharpIDE.SourceGenerators", "..\SharpIDE.SourceGenerators\SharpIDE.SourceGenerators.csproj", "{3EE9E01D-5C04-4C6D-89DD-D85F1F527976}"
+EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
@@ -44,5 +46,11 @@ Global
{DFF170D9-D92E-4DB7-83B5-19640EAF79D2}.ExportDebug|Any CPU.Build.0 = Debug|Any CPU
{DFF170D9-D92E-4DB7-83B5-19640EAF79D2}.ExportRelease|Any CPU.ActiveCfg = Debug|Any CPU
{DFF170D9-D92E-4DB7-83B5-19640EAF79D2}.ExportRelease|Any CPU.Build.0 = Debug|Any CPU
+ {3EE9E01D-5C04-4C6D-89DD-D85F1F527976}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3EE9E01D-5C04-4C6D-89DD-D85F1F527976}.Debug|Any CPU.Build.0 = Debug|Any CPU
+ {3EE9E01D-5C04-4C6D-89DD-D85F1F527976}.ExportDebug|Any CPU.ActiveCfg = Debug|Any CPU
+ {3EE9E01D-5C04-4C6D-89DD-D85F1F527976}.ExportDebug|Any CPU.Build.0 = Debug|Any CPU
+ {3EE9E01D-5C04-4C6D-89DD-D85F1F527976}.ExportRelease|Any CPU.ActiveCfg = Debug|Any CPU
+ {3EE9E01D-5C04-4C6D-89DD-D85F1F527976}.ExportRelease|Any CPU.Build.0 = Debug|Any CPU
EndGlobalSection
EndGlobal
diff --git a/src/SharpIDE.Godot/InputStringNames.cs b/src/SharpIDE.Godot/ThemeStringNames.cs
similarity index 54%
rename from src/SharpIDE.Godot/InputStringNames.cs
rename to src/SharpIDE.Godot/ThemeStringNames.cs
index 7907e5f2..a37086ec 100644
--- a/src/SharpIDE.Godot/InputStringNames.cs
+++ b/src/SharpIDE.Godot/ThemeStringNames.cs
@@ -2,17 +2,6 @@
namespace SharpIDE.Godot;
-public static class InputStringNames
-{
- public static readonly StringName RenameSymbol = nameof(RenameSymbol);
- public static readonly StringName CodeFixes = nameof(CodeFixes);
- public static readonly StringName StepOver = nameof(StepOver);
- public static readonly StringName FindInFiles = nameof(FindInFiles);
- public static readonly StringName FindFiles = nameof(FindFiles);
- public static readonly StringName SaveFile = nameof(SaveFile);
- public static readonly StringName SaveAllFiles = nameof(SaveAllFiles);
-}
-
public static class ThemeStringNames
{
public static readonly StringName Font = "font";
diff --git a/src/SharpIDE.Godot/InputStringNames.cs.uid b/src/SharpIDE.Godot/ThemeStringNames.cs.uid
similarity index 100%
rename from src/SharpIDE.Godot/InputStringNames.cs.uid
rename to src/SharpIDE.Godot/ThemeStringNames.cs.uid
diff --git a/src/SharpIDE.SourceGenerators/InputMapName/InputMapNameConstants.cs b/src/SharpIDE.SourceGenerators/InputMapName/InputMapNameConstants.cs
new file mode 100644
index 00000000..29432977
--- /dev/null
+++ b/src/SharpIDE.SourceGenerators/InputMapName/InputMapNameConstants.cs
@@ -0,0 +1,16 @@
+namespace SharpIDE.SourceGenerators.InputMapName;
+
+public class InputMapNameConstants
+{
+ public const string PropertiesPlaceholder = "{Properties}";
+ public const string Code = $$"""
+ //
+ using Godot;
+ namespace SharpIDE.Godot;
+
+ public static partial class InputStringNames
+ {
+ {{PropertiesPlaceholder}}
+ }
+ """;
+}
diff --git a/src/SharpIDE.SourceGenerators/InputMapName/InputMapNameGenerator.cs b/src/SharpIDE.SourceGenerators/InputMapName/InputMapNameGenerator.cs
new file mode 100644
index 00000000..67f3204d
--- /dev/null
+++ b/src/SharpIDE.SourceGenerators/InputMapName/InputMapNameGenerator.cs
@@ -0,0 +1,67 @@
+using System;
+using System.Collections.Generic;
+using System.IO;
+using System.Linq;
+using System.Text.RegularExpressions;
+using Microsoft.CodeAnalysis;
+
+namespace SharpIDE.SourceGenerators.InputMapName;
+
+[Generator]
+public class InputMapNameGenerator : IIncrementalGenerator
+{
+ private static readonly Regex _inputNameRegex = new(@"\b(\w+)\s*=\s*\{", RegexOptions.Compiled);
+
+ public void Initialize(IncrementalGeneratorInitializationContext context)
+ {
+ var inputMapNamesProvider = context.AdditionalTextsProvider
+ .Where(at => Path.GetFileName(at.Path).Equals("project.godot", StringComparison.OrdinalIgnoreCase));
+ context.RegisterSourceOutput(inputMapNamesProvider, GenerateInputMapNames);
+ }
+
+ private static void GenerateInputMapNames(SourceProductionContext context, AdditionalText additionalText)
+ {
+ var text = additionalText.GetText(context.CancellationToken);
+ if (text is null)
+ return;
+
+ // Parse the input map names from the project.godot file
+ var matchCollection = _inputNameRegex.Matches(text.ToString());
+ var inputMapNames = matchCollection.Cast().Select(match => match.Groups[1].Value).ToList();
+
+ // Generate the source code
+ var sourceText = InputMapNameConstants.Code.Replace(InputMapNameConstants.PropertiesPlaceholder, GenerateProperties(inputMapNames));
+
+ // Add the source code to the compilation
+ context.AddSource("InputMapNames.g.cs", sourceText);
+ }
+
+ private static string GenerateProperties(IEnumerable inputMapNames)
+ {
+ var propertiesBuilder = new System.Text.StringBuilder();
+ foreach (var name in inputMapNames)
+ {
+ var safeName = SanitizeIdentifier(name);
+ propertiesBuilder.AppendLine($"\tpublic const string {safeName} = \"{name}\";");
+ }
+ return propertiesBuilder.ToString();
+ }
+ ///
+ /// Sanitizes a string to be a valid C# identifier.
+ /// Replaces invalid characters with underscores and ensures the identifier does not start with a digit.
+ ///
+ private static string SanitizeIdentifier(string name)
+ {
+ if (string.IsNullOrEmpty(name))
+ return "_";
+
+ // Remove invalid characters
+ var sanitized = Regex.Replace(name, "[^a-zA-Z0-9_]", "");
+
+ // If the first character is not a letter or underscore, prefix with underscore
+ if (!char.IsLetter(sanitized, 0) && sanitized[0] != '_')
+ sanitized = "_" + sanitized;
+
+ return sanitized;
+ }
+}
diff --git a/src/SharpIDE.SourceGenerators/Properties/launchSettings.json b/src/SharpIDE.SourceGenerators/Properties/launchSettings.json
new file mode 100644
index 00000000..3c38c4ac
--- /dev/null
+++ b/src/SharpIDE.SourceGenerators/Properties/launchSettings.json
@@ -0,0 +1,9 @@
+{
+ "$schema": "http://json.schemastore.org/launchsettings.json",
+ "profiles": {
+ "SharpIDE.SourceGenerators": {
+ "commandName": "DebugRoslynComponent",
+ "targetProject": "../SharpIDE.Godot/SharpIDE.Godot.csproj"
+ }
+ }
+}
diff --git a/src/SharpIDE.SourceGenerators/SharpIDE.SourceGenerators.csproj b/src/SharpIDE.SourceGenerators/SharpIDE.SourceGenerators.csproj
new file mode 100644
index 00000000..c5d3962f
--- /dev/null
+++ b/src/SharpIDE.SourceGenerators/SharpIDE.SourceGenerators.csproj
@@ -0,0 +1,26 @@
+
+
+
+ false
+ false
+ netstandard2.0
+ false
+ enable
+ latest
+
+ true
+ true
+
+ SharpIDE.SourceGenerators
+ SharpIDE.SourceGenerators
+
+
+
+
+ all
+ runtime; build; native; contentfiles; analyzers; buildtransitive
+
+
+
+
+