Repository navigation
feat: add opt-in field.name strategies and JavaBeans accessor naming - #1494
Conversation
|
📦 Plugin has been packaged for this PR. You can download it from the GitHub Actions workflow run by clicking on the "Artifacts" dropdown:
Each artifact is a ready-to-install plugin zip — pick the one matching your IDEA version and install it via Settings → Plugins → ⚙ → Install Plugin from Disk. No extraction needed. |
Codecov Report❌ Patch coverage is Additional details and impacted files@@ Coverage Diff @@
## master #1494 +/- ##
=============================================
+ Coverage 70.037% 70.102% +0.065%
=============================================
Files 523 525 +2
Lines 30421 30484 +63
Branches 7149 7173 +24
=============================================
+ Hits 21306 21370 +64
+ Misses 6363 6350 -13
- Partials 2752 2764 +12
Flags with carried forward coverage won't be shown. Click here to find out more.
... and 6 files with indirect coverage changes Continue to review full report in Codecov by Harness.
🚀 New features to boost your workflow:
|
|
📦 Plugin has been packaged for this PR. You can download it from the GitHub Actions workflow run by clicking on the "Artifacts" dropdown:
Each artifact is a ready-to-install plugin zip — pick the one matching your IDEA version and install it via Settings → Plugins → ⚙ → Install Plugin from Disk. No extraction needed. |
581722b to
b312218
Compare
|
📦 Plugin has been packaged for this PR. You can download it from the GitHub Actions workflow run by clicking on the "Artifacts" dropdown:
Each artifact is a ready-to-install plugin zip — pick the one matching your IDEA version and install it via Settings → Plugins → ⚙ → Install Plugin from Disk. No extraction needed. |
b312218 to
e886305
Compare
|
📦 Plugin has been packaged for this PR. You can download it from the GitHub Actions workflow run by clicking on the "Artifacts" dropdown:
Each artifact is a ready-to-install plugin zip — pick the one matching your IDEA version and install it via Settings → Plugins → ⚙ → Install Plugin from Disk. No extraction needed. |
e886305 to
7ff2a8d
Compare
|
📦 Plugin has been packaged for this PR. You can download it from the GitHub Actions workflow run by clicking on the "Artifacts" dropdown:
Each artifact is a ready-to-install plugin zip — pick the one matching your IDEA version and install it via Settings → Plugins → ⚙ → Install Plugin from Disk. No extraction needed. |
7ff2a8d to
d70bded
Compare
|
📦 Plugin has been packaged for this PR. You can download it from the GitHub Actions workflow run by clicking on the "Artifacts" dropdown:
Each artifact is a ready-to-install plugin zip — pick the one matching your IDEA version and install it via Settings → Plugins → ⚙ → Install Plugin from Disk. No extraction needed. |
d70bded to
a36295a
Compare
|
📦 Plugin has been packaged for this PR. You can download it from the GitHub Actions workflow run by clicking on the "Artifacts" dropdown:
Each artifact is a ready-to-install plugin zip — pick the one matching your IDEA version and install it via Settings → Plugins → ⚙ → Install Plugin from Disk. No extraction needed. |
v2 shipped selectable naming strategy blocks (#[Jackson_SnakeCaseStrategy], KebabCaseStrategy, ...) in its recommended config. The v3 rewrite replaced those blocks with built-in extensions but did not port the strategies, even though helpers such as capitalize, uncapitalize, toCamelCase and camel2Underline are already exposed as `tool` in Groovy rules. Add nine default-disabled field.name.* extensions: uncapitalize, capitalize, camel-case, snake-case, kebab-case, pascal-case, lower-dot-case, upper-snake-case and upper-kebab-case. Each is a single field.name Groovy rule calling one `tool` helper with `it.fieldName()` as input. The existing camel-case extension already produces lowerCamelCase, so no duplicate lower-camel-case extension is added. A cross-ecosystem survey shows one convergent converter rule: a run of capitals is one word, except that its last letter starts the next word when a lower-case letter follows (URLValue -> url_value / url-value / urlValue). This is the boundary used by System.Text.Json, Newtonsoft.Json, Rails, Pydantic, lodash, heck and Jackson's KebabCaseStrategy. Jackson's SnakeCaseStrategy (urlvalue) is the outlier; camel2Underline keeps that legacy behaviour for rules that call it by name, but the extensions use the mainstream splitter. Style guides disagree on how to write the source identifier, while converters normalize it to the same word sequence. Add toSnakeCase, toPascalCase and toLowerDotCase to RuleToolUtils, all sharing the nameWords() splitter with toCamelCase / toKebabCase. field.name.snake-case now yields url_value; the previously documented intentional snake/kebab acronym difference is removed. Three new tool signatures are reflected in the regenerated script-context catalog. Add BeanPropertyNames implementing java.beans.Introspector.decapitalize and use it in DefaultPsiClassHelper (getter/setter collection) and ScriptPsiMethodContext.fieldName(). getURLValue() now resolves to URLValue instead of uRLValue, so a URLValue field and its accessor collide and export one property. This is also the standard bean naming Jackson 3 defaults to. No new rule key, parser, setting, or config syntax is added. For a generated class with `public String Barcode` and `getBarcode()`, enabling uncapitalize maps the field to `barcode`; the getter collides and is skipped by the model builder's existing name-based de-duplication (#1492). Fixes: #1492
a36295a to
09526d8
Compare
|
📦 Plugin has been packaged for this PR. You can download it from the GitHub Actions workflow run by clicking on the "Artifacts" dropdown:
Each artifact is a ready-to-install plugin zip — pick the one matching your IDEA version and install it via Settings → Plugins → ⚙ → Install Plugin from Disk. No extraction needed. |
|
A PR (#1496) has been opened to fix this issue. You can download the packaged plugin from the GitHub Actions workflow run by clicking on the "Artifacts" dropdown:
Each artifact is a ready-to-install plugin zip — pick the one matching your IDEA version and install it via Settings → Plugins → ⚙ → Install Plugin from Disk. No extraction needed. |
Fixes #1492
Summary
Thrift-generated models declare a public field (
public String Barcode) alongsidegetBarcode(). easy-yapi exports both, asBarcodeandbarcode. This PR adds nine opt-infield.name.*extensions for picking a naming strategy, and aligns both the strategy helpers and accessor property-name derivation with the mainstream convention. Enablingfield.name.uncapitalizestill makes that model export a singlebarcodeproperty.What "mainstream" means here
A cross-ecosystem survey (Jackson, Gson, System.Text.Json, Newtonsoft.Json, lodash, change-case, Pydantic, Rails, heck, serde, Symfony, protobuf, plus the Google / Microsoft / Go style guides) shows one convergent rule at the converter layer: a run of capitals is one word, except that its last letter starts the next word when a lower-case letter follows.
URLValuetherefore becomesurl_value/url-value/urlValue/UrlValue/url.value. This is literally the same boundary rule as .NET'sJsonSeparatorNamingPolicy, Newtonsoft'sToSeparatedCase, Rails'underscore, Pydantic'sto_snake, lodash'swords()and Jackson'sKebabCaseStrategy(upperCount).Jackson's
SnakeCaseStrategyis the outlier (urlvalue). The legacytool.camel2Underlinekeeps that behaviour for rules that call it by name, but built-in extensions now use the mainstream splitter. Style guides disagree on how to write the source identifier (XmlHttpRequestvsServeHTTPvsIPAddress), but every converter normalizes it to the same word sequence — the divergence is at the naming layer, not the conversion layer.For accessor names,
java.beans.Introspector.decapitalizeis the platform rule: two leading capitals are preserved, sogetURLValue()->URLValue. This is also Jackson 3's default (Jackson 2 needsUSE_STD_BEAN_NAMING). It is the only choice that lets aURLValuefield and its getter collide and de-duplicate; the .NET/JSurlValuespelling would still export two properties.Changes
default-enabled: falseextensions, all named after the rule key they configure:field.name.uncapitalize,capitalize,camel-case,snake-case,kebab-case,pascal-case,lower-dot-case,upper-snake-case,upper-kebab-case. Each holds onefield.nameGroovy rule calling a singletoolhelper withit.fieldName(). The existingcamel-casealready produces lowerCamelCase, so no duplicatelower-camel-caseextension is added.RuleToolUtils.kt: addstoSnakeCase,toPascalCaseandtoLowerDotCasealongsidetoCamelCase/toKebabCase; all five share thenameWords()splitter.camel2Underlineis unchanged but is now documented as the JacksonSnakeCaseStrategylegacy and is no longer used by an extension.field.name.snake-casenow yieldsurl_valueinstead ofurlvalue. The previously documented intentional snake/kebab acronym difference is removed.BeanPropertyNames.kt: a shareddecapitalize/fromAccessorimplementingIntrospector.decapitalize.DefaultPsiClassHelperuses it for getter and setter collection, andScriptPsiMethodContext.fieldName()uses it for rules.getURLValue()now resolves toURLValueinstead ofuRLValue, so an acronym field and its accessor export one property ([BUG] A public field and its getter are exported as two duplicate fields (regression from #1443) #1492 class of bug).skills/easy-yapi-assistant/rule-contexts.json/.mdvia./gradlew syncRuleContexts(three newtoolsignatures).RuleToolUtilsunit tests per helper, a script-contextfieldName()test covering field / getter / setter /is/ acronym / plain methods, and an integration test asserting exactly one property forURLValue+getURLValue().User-facing result
Enable a strategy in Settings -> Extensions. For #1492 with
field.name.uncapitalize:Barcode,barcodebarcodeFor a
URLValuefield withgetURLValue()(no extension needed):URLValue,uRLValueURLValueNothing changes for anyone who leaves the extensions disabled.
Boundaries
field.nameusesSINGLEaggregation: a project-level.easyapi/*.rulesentry (priority 4) overrides a built-in extension (priority 3), andjackson's@JsonPropertyrule still wins for explicitly annotated members.capitalize/uncapitalizestay character-level v2 helpers (URLValue -> uRLValue), not naming strategies; usecamel-case/pascal-casefor acronym-aware results. They are kept becauseuncapitalizeis the [BUG] A public field and its getter are exported as two duplicate fields (regression from #1443) #1492 fix.getURL() -> url) is intentionally not reproduced; this PR follows the standard bean naming that Jackson 3 defaults to.isSetBarcode()is still treated as a boolean getter and exported as a separatesetBarcodeproperty — an independent getter-detection issue (Jackson does the same), out of scope here.Verification
RuleToolUtilstests pinning both the mainstream and the legacy Jackson outputs, a script-contextfieldName()unit test, an acronym accessor integration test, and the 39-extension ledger test pass../gradlew syncRuleContextskeeps the generated script-context catalog in sync with the reflected context API (EasyYapiAssistantSkillTestfreshness guard passes)../gradlew test: 747 test classes, 7642 tests, 0 failures, 0 errors (10 skipped).