Skip to content

Commit 744fb2d

Browse files
committed
small fixes
1 parent 369db23 commit 744fb2d

7 files changed

Lines changed: 249 additions & 11 deletions

File tree

README-RIDER.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,12 @@ export JAVA_HOME=$(/usr/libexec/java_home -v 17) # macOS
106106
3. Выберите `output/TypeScriptDefinitionGenerator.Rider-<version>.zip`
107107
4. Перезапустите Rider
108108

109+
### Настройки
110+
111+
**Settings** (Ctrl+Alt+S / Cmd+,) → **Tools****TypeScript Definition Generator**. Или в поле поиска настроек введите «TypeScript» — откроется страница плагина.
112+
113+
Глобальные настройки генерации (camelCase, module, EOL и т.д.). Сохраняются в конфигурации IDE. При каждой генерации из меню плагин записывает эти настройки в `tsdefgen.json` в корне проекта — CLI использует их.
114+
109115
### Использование
110116

111117
1. Откройте решение TypeScriptDefinitionGenerator (или любое с CLI в `src/TypeScriptDefinitionGenerator.Cli`)

rider/src/main/kotlin/com/typescriptdefinitiongenerator/rider/GenerateTypeScriptDefinitionAction.kt

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -56,6 +56,7 @@ class GenerateTypeScriptDefinitionAction : AnAction() {
5656
}
5757

5858
try {
59+
ensureTsDefGenJson(File(filePath), solutionDir)
5960
val process = ProcessBuilder(
6061
dotnetPath, "run", "--project", cliProjectPath.absolutePath, "--", filePath
6162
)
@@ -143,8 +144,9 @@ class GenerateTypeScriptDefinitionAction : AnAction() {
143144
val originalContent = csproj.readText(Charsets.UTF_8)
144145
var content = originalContent
145146
val sourceItem = """<TypeScriptDefinitionSource Include="$relativeSource" />"""
147+
val sourceFileName = sourceFile.name
146148
val noneItem = """<None Update="$relativeGenerated">
147-
<DependentUpon>$relativeSource</DependentUpon>
149+
<DependentUpon>$sourceFileName</DependentUpon>
148150
</None>"""
149151
if (!content.contains("TypeScriptDefinitionSource Include=\"$relativeSource\"")) {
150152
content = addOrCreateItemGroup(content, "TypeScriptDefinitionSource", sourceItem)
@@ -188,11 +190,9 @@ class GenerateTypeScriptDefinitionAction : AnAction() {
188190
val match = itemGroupRegex.findAll(content).firstOrNull { m ->
189191
itemRegex.containsMatchIn(m.groupValues[2])
190192
} ?: run {
191-
return content.replace("</Project>", """
192-
<ItemGroup>
193-
$newItem
194-
</ItemGroup>
195-
</Project>""")
193+
return content.replace(Regex("""(\s*)</Project>""")) {
194+
"\n\n <ItemGroup>\n $newItem\n </ItemGroup>\n</Project>"
195+
}
196196
}
197197
val (prefix, block, suffix) = match.destructured
198198
val existingItems = itemRegex.findAll(block).map { it.value }.toList()
@@ -204,6 +204,28 @@ class GenerateTypeScriptDefinitionAction : AnAction() {
204204
return content.replace(match.value, newItemGroup)
205205
}
206206

207+
private fun ensureTsDefGenJson(sourceFile: File, solutionDir: String) {
208+
val projectDir = findCsprojDirectory(sourceFile) ?: return
209+
val tsdefgen = File(projectDir, "tsdefgen.json")
210+
val s = TsDefGenSettingsService.getInstance().state
211+
val json = """{
212+
"camelCaseEnumerationValues": ${s.camelCaseEnumerationValues},
213+
"camelCasePropertyNames": ${s.camelCasePropertyNames},
214+
"camelCaseTypeNames": ${s.camelCaseTypeNames},
215+
"webEssentials2015": ${s.webEssentials2015},
216+
"classInsteadOfInterface": ${s.classInsteadOfInterface},
217+
"defaultModuleName": "${s.defaultModuleName.replace("\\", "\\\\").replace("\"", "\\\"")}",
218+
"useNamespace": ${s.useNamespace},
219+
"declareModule": ${s.declareModule},
220+
"ignoreIntellisense": ${s.ignoreIntellisense},
221+
"eolType": "${s.eolType}",
222+
"indentTab": ${s.indentTab},
223+
"indentTabSize": ${s.indentTabSize}
224+
}
225+
"""
226+
tsdefgen.writeText(json, Charsets.UTF_8)
227+
}
228+
207229
private fun findCsprojDirectory(file: File): File? {
208230
var dir = file.parentFile ?: return null
209231
while (dir != null) {
Lines changed: 43 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,43 @@
1+
package com.typescriptdefinitiongenerator.rider
2+
3+
import com.intellij.openapi.components.PersistentStateComponent
4+
import com.intellij.openapi.components.Service
5+
import com.intellij.openapi.components.State
6+
import com.intellij.openapi.components.Storage
7+
8+
enum class EOLType {
9+
LF,
10+
CRLF
11+
}
12+
13+
data class TsDefGenSettingsState(
14+
var camelCaseEnumerationValues: Boolean = false,
15+
var camelCasePropertyNames: Boolean = true,
16+
var camelCaseTypeNames: Boolean = false,
17+
var webEssentials2015: Boolean = false,
18+
var classInsteadOfInterface: Boolean = false,
19+
var defaultModuleName: String = "Server.Dtos",
20+
var useNamespace: Boolean = true,
21+
var declareModule: Boolean = true,
22+
var ignoreIntellisense: Boolean = true,
23+
var eolType: String = "LF",
24+
var indentTab: Boolean = true,
25+
var indentTabSize: Int = 2
26+
)
27+
28+
@Service(Service.Level.APP)
29+
@State(name = "TsDefGenSettings", storages = [Storage("tsdefgen.xml")])
30+
class TsDefGenSettingsService : PersistentStateComponent<TsDefGenSettingsState> {
31+
private var state = TsDefGenSettingsState()
32+
33+
override fun getState(): TsDefGenSettingsState = state
34+
35+
override fun loadState(state: TsDefGenSettingsState) {
36+
this.state = state
37+
}
38+
39+
companion object {
40+
fun getInstance(): TsDefGenSettingsService =
41+
com.intellij.openapi.application.ApplicationManager.getApplication().getService(TsDefGenSettingsService::class.java)
42+
}
43+
}
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
1+
package com.typescriptdefinitiongenerator.rider
2+
3+
import com.intellij.openapi.options.Configurable
4+
import com.intellij.openapi.options.SearchableConfigurable
5+
import com.intellij.ui.components.JBCheckBox
6+
import com.intellij.ui.components.JBTextField
7+
import com.intellij.util.ui.JBUI
8+
import java.awt.BorderLayout
9+
import java.awt.GridBagConstraints
10+
import java.awt.GridBagLayout
11+
import java.awt.Insets
12+
import javax.swing.JComponent
13+
import javax.swing.JLabel
14+
import javax.swing.JPanel
15+
import javax.swing.JSeparator
16+
import javax.swing.JSpinner
17+
import javax.swing.SpinnerNumberModel
18+
19+
class TsDefGenSettingsConfigurable : SearchableConfigurable {
20+
21+
private var mainPanel: JPanel? = null
22+
private lateinit var camelCaseEnumerationValues: JBCheckBox
23+
private lateinit var camelCasePropertyNames: JBCheckBox
24+
private lateinit var camelCaseTypeNames: JBCheckBox
25+
private lateinit var webEssentials2015: JBCheckBox
26+
private lateinit var classInsteadOfInterface: JBCheckBox
27+
private lateinit var defaultModuleName: JBTextField
28+
private lateinit var useNamespace: JBCheckBox
29+
private lateinit var declareModule: JBCheckBox
30+
private lateinit var ignoreIntellisense: JBCheckBox
31+
private lateinit var eolType: javax.swing.JComboBox<EOLType>
32+
private lateinit var indentTab: JBCheckBox
33+
private lateinit var indentTabSize: JSpinner
34+
35+
override fun getId(): String = "com.typescriptdefinitiongenerator.rider.TsDefGenSettingsConfigurable"
36+
37+
override fun getDisplayName(): String = "TypeScript Definition Generator"
38+
39+
override fun createComponent(): JComponent {
40+
val settings = TsDefGenSettingsService.getInstance().state
41+
camelCaseEnumerationValues = JBCheckBox("Camel case enum values", settings.camelCaseEnumerationValues)
42+
camelCasePropertyNames = JBCheckBox("Camel case property names", settings.camelCasePropertyNames)
43+
camelCaseTypeNames = JBCheckBox("Camel case type names", settings.camelCaseTypeNames)
44+
webEssentials2015 = JBCheckBox("Web Essentials 2015 file names (<filename>.cs.d.ts)", settings.webEssentials2015)
45+
classInsteadOfInterface = JBCheckBox("Class instead of Interface", settings.classInsteadOfInterface)
46+
defaultModuleName = JBTextField(settings.defaultModuleName, 30)
47+
useNamespace = JBCheckBox("Use Namespace", settings.useNamespace)
48+
declareModule = JBCheckBox("Declare module", settings.declareModule)
49+
ignoreIntellisense = JBCheckBox("Ignore intellisense for client side reference names", settings.ignoreIntellisense)
50+
eolType = javax.swing.JComboBox<EOLType>(EOLType.entries.toTypedArray()).apply {
51+
selectedItem = EOLType.entries.find { it.name == settings.eolType } ?: EOLType.LF
52+
}
53+
indentTab = JBCheckBox("Indent Tab (use Space when unchecked)", settings.indentTab)
54+
indentTabSize = JSpinner(SpinnerNumberModel(settings.indentTabSize.coerceIn(1, 8), 1, 8, 1))
55+
56+
defaultModuleName.toolTipText = "Set the top-level module name for the generated .d.ts file. Default is \"Server.Dtos\""
57+
useNamespace.toolTipText = "Use Namespace by default, otherwise \"Default Module name\" will be taken."
58+
classInsteadOfInterface.toolTipText = "Controls whether to generate a class or an interface: default is an Interface"
59+
declareModule.toolTipText = "Controls whether to generate types in declared module or without one, but with export"
60+
ignoreIntellisense.toolTipText = "Ignore intellisense for client side reference names"
61+
indentTab.toolTipText = "Choose indentation to use Tab/Space: default is Tab"
62+
indentTabSize.toolTipText = "Set amount Spaces to replace the Tab, when Indent Tab is off"
63+
mainPanel = JPanel(GridBagLayout()).apply {
64+
val c = GridBagConstraints().apply { fill = GridBagConstraints.HORIZONTAL; weightx = 1.0; insets = Insets(2, 0, 2, 0) }
65+
fun addRow(comp: JComponent) { c.gridwidth = GridBagConstraints.REMAINDER; add(comp, c) }
66+
fun addLabeled(label: String, comp: JComponent) {
67+
c.gridwidth = 1; c.weightx = 0.0; add(JLabel(label), c)
68+
c.gridwidth = GridBagConstraints.REMAINDER; c.weightx = 1.0; add(comp, c)
69+
}
70+
addRow(JLabel("Casing").apply { font = font.deriveFont(java.awt.Font.BOLD) })
71+
addRow(camelCaseEnumerationValues)
72+
addRow(camelCasePropertyNames)
73+
addRow(camelCaseTypeNames)
74+
addRow(JSeparator())
75+
addRow(JLabel("Compatibility").apply { font = font.deriveFont(java.awt.Font.BOLD) })
76+
addRow(webEssentials2015)
77+
addRow(JSeparator())
78+
addRow(JLabel("Settings").apply { font = font.deriveFont(java.awt.Font.BOLD) })
79+
addLabeled("Default Module name:", defaultModuleName)
80+
addRow(useNamespace)
81+
addRow(classInsteadOfInterface)
82+
addRow(declareModule)
83+
addRow(ignoreIntellisense)
84+
addLabeled("End Of Line (EOL):", eolType)
85+
addRow(indentTab)
86+
addLabeled("Indent Tab Size:", indentTabSize)
87+
border = JBUI.Borders.empty(10, 10, 10, 10)
88+
}
89+
return JPanel(BorderLayout()).apply { add(mainPanel, BorderLayout.CENTER) }
90+
}
91+
92+
override fun isModified(): Boolean {
93+
val s = TsDefGenSettingsService.getInstance().state
94+
return camelCaseEnumerationValues.isSelected != s.camelCaseEnumerationValues ||
95+
camelCasePropertyNames.isSelected != s.camelCasePropertyNames ||
96+
camelCaseTypeNames.isSelected != s.camelCaseTypeNames ||
97+
webEssentials2015.isSelected != s.webEssentials2015 ||
98+
classInsteadOfInterface.isSelected != s.classInsteadOfInterface ||
99+
defaultModuleName.text != s.defaultModuleName ||
100+
useNamespace.isSelected != s.useNamespace ||
101+
declareModule.isSelected != s.declareModule ||
102+
ignoreIntellisense.isSelected != s.ignoreIntellisense ||
103+
(eolType.selectedItem as EOLType).name != s.eolType ||
104+
indentTab.isSelected != s.indentTab ||
105+
(indentTabSize.value as Int) != s.indentTabSize
106+
}
107+
108+
override fun apply() {
109+
val s = TsDefGenSettingsService.getInstance().state
110+
s.camelCaseEnumerationValues = camelCaseEnumerationValues.isSelected
111+
s.camelCasePropertyNames = camelCasePropertyNames.isSelected
112+
s.camelCaseTypeNames = camelCaseTypeNames.isSelected
113+
s.webEssentials2015 = webEssentials2015.isSelected
114+
s.classInsteadOfInterface = classInsteadOfInterface.isSelected
115+
s.defaultModuleName = defaultModuleName.text.trim().ifEmpty { "Server.Dtos" }
116+
s.useNamespace = useNamespace.isSelected
117+
s.declareModule = declareModule.isSelected
118+
s.ignoreIntellisense = ignoreIntellisense.isSelected
119+
s.eolType = (eolType.selectedItem as EOLType).name
120+
s.indentTab = indentTab.isSelected
121+
s.indentTabSize = (indentTabSize.value as Int).coerceIn(1, 8)
122+
}
123+
124+
override fun reset() {
125+
val s = TsDefGenSettingsService.getInstance().state
126+
camelCaseEnumerationValues.isSelected = s.camelCaseEnumerationValues
127+
camelCasePropertyNames.isSelected = s.camelCasePropertyNames
128+
camelCaseTypeNames.isSelected = s.camelCaseTypeNames
129+
webEssentials2015.isSelected = s.webEssentials2015
130+
classInsteadOfInterface.isSelected = s.classInsteadOfInterface
131+
defaultModuleName.text = s.defaultModuleName
132+
useNamespace.isSelected = s.useNamespace
133+
declareModule.isSelected = s.declareModule
134+
ignoreIntellisense.isSelected = s.ignoreIntellisense
135+
eolType.selectedItem = EOLType.entries.find { it.name == s.eolType } ?: EOLType.LF
136+
indentTab.isSelected = s.indentTab
137+
indentTabSize.value = s.indentTabSize
138+
}
139+
140+
override fun disposeUIResources() {
141+
mainPanel = null
142+
}
143+
}

rider/src/main/resources/META-INF/plugin.xml

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,14 @@
55
<description>Generate .d.ts files from C# DTO models. Right-click on a .cs file and select "Generate TypeScript Definition" to create a TypeScript definition file.</description>
66
<depends>com.intellij.modules.rider</depends>
77

8+
<extensions defaultExtensionNs="com.intellij">
9+
<applicationConfigurable
10+
parentId="tools"
11+
instance="com.typescriptdefinitiongenerator.rider.TsDefGenSettingsConfigurable"
12+
id="com.typescriptdefinitiongenerator.rider.TsDefGenSettingsConfigurable"
13+
displayName="TypeScript Definition Generator"/>
14+
</extensions>
15+
816
<actions>
917
<action id="TypeScriptDefinitionGenerator.GenerateTypeScriptDefinition"
1018
class="com.typescriptdefinitiongenerator.rider.GenerateTypeScriptDefinitionAction"

src/TypeScriptDefinitionGenerator.Core/OptionsLoader.cs

Lines changed: 15 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -17,8 +17,8 @@ public static class OptionsLoader
1717

1818
public static IGeneratorOptions LoadFromProjectDirectory(string projectDirectory)
1919
{
20-
var jsonPath = Path.Combine(projectDirectory, OverrideFileName);
21-
if (!File.Exists(jsonPath))
20+
var jsonPath = FindOverrideFile(projectDirectory);
21+
if (jsonPath == null)
2222
return new GeneratorOptions();
2323

2424
try
@@ -32,4 +32,17 @@ public static IGeneratorOptions LoadFromProjectDirectory(string projectDirectory
3232
return new GeneratorOptions();
3333
}
3434
}
35+
36+
private static string? FindOverrideFile(string startDirectory)
37+
{
38+
var dir = startDirectory;
39+
while (!string.IsNullOrEmpty(dir))
40+
{
41+
var path = Path.Combine(dir, OverrideFileName);
42+
if (File.Exists(path))
43+
return path;
44+
dir = Path.GetDirectoryName(dir);
45+
}
46+
return null;
47+
}
3548
}

tests/ClassLibrary1/ClassLibrary1.csproj

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -10,10 +10,10 @@
1010
<Platforms>AnyCPU</Platforms>
1111
<NoWarn>1701;1702;1591;CS8618;NU1701;</NoWarn>
1212
</PropertyGroup>
13-
14-
13+
1514
<ItemGroup>
1615
<TypeScriptDefinitionSource Include="SecondClass.cs" />
16+
<TypeScriptDefinitionSource Include="SomeClassDto.cs" />
1717
<TypeScriptDefinitionSource Include="SomeEnum.cs" />
1818
<TypeScriptDefinitionSource Include="base\ThirdClass.cs" />
1919
</ItemGroup>
@@ -22,11 +22,14 @@
2222
<None Update="SecondClass.generated.d.ts">
2323
<DependentUpon>SecondClass.cs</DependentUpon>
2424
</None>
25+
<None Update="SomeClassDto.generated.d.ts">
26+
<DependentUpon>SomeClassDto.cs</DependentUpon>
27+
</None>
2528
<None Update="SomeEnum.generated.d.ts">
2629
<DependentUpon>SomeEnum.cs</DependentUpon>
2730
</None>
2831
<None Update="base\ThirdClass.generated.d.ts">
29-
<DependentUpon>base\ThirdClass.cs</DependentUpon>
32+
<DependentUpon>ThirdClass.cs</DependentUpon>
3033
</None>
3134
</ItemGroup>
3235
</Project>

0 commit comments

Comments
 (0)