feat(analysis): add DomainLanguageParser emitting cc.json 2.0 domain … - #4507
feat(analysis): add DomainLanguageParser emitting cc.json 2.0 domain …#4507ChristianHuehn wants to merge 75 commits into
Conversation
…lens
Port the DomainLanguageCharta analyzer into ccsh as a first-class parser
(`ccsh domainlanguageparser`) that extracts domain vocabulary (word
frequencies, optional n-grams, TF-IDF) from source code and writes it into
the reserved cc.json 2.0 `domain` lens, keyed by node id over the standard
files tree.
- New writer (DomainProjectGenerator) builds a Project + opaque `domain`
lens; keys computed with NodeId.fromSegments so they resolve against the
ids the 2.0 writer emits (File leaves, Folder dirs, empty-segment root).
tfidf omitted when null; deterministic, byte-stable output.
- picocli command (CommonAnalyserParameters + AnalyserInterface) with a
Dialog for interactive mode; registered in Ccsh.kt, ccsh build, settings,
and the ccsh analyser tests. Not in AttributeGeneratorRegistry (opaque
lens, not numeric metrics).
- Reuses the ported analysis engine verbatim; retires DLC's {tree, words}
JSON output and kotlinx-cli entrypoint.
- Dependency dedupe: drop jgit (-> AnalyserInterface GitignoreHandler),
mordant (-> model ProgressTracker), kotlinx-cli and the kotlin
serialization plugin; keep kasechange and kotlinx-serialization-json.
- Docs: analysis README parser table, module README, CHANGELOG, gh-pages
parser page + sidebar.
Verified equivalent to the original DLC tool: same word/frequency/TF-IDF
data on identical inputs; domain lens survives a mergefilter round-trip.
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
|
Important Review skippedToo many files! This PR contains 599 files, which is 449 over the limit of 150. To get a review, reduce the PR to 150 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. Usage-priced reviews support at most 300 files. ⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Team Run ID: ⛔ Files ignored due to path filters (3)
📒 Files selected for processing (599)
You can disable this status message by setting the ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
…in view The non-map race branch skipped the very file-state emission that raised the indicator, then waited for a subsequent one. A file-panel change is usually the last emission there is, so on /domain the spinner hung until the 60s deadline. Distinguish the two raisers: a file-panel change has already landed and settles on the current file set, while a load raises the indicator before the fetch and must wait for filesLoaded first. isPendingHeavyDispatch$ had the same shape of defect: only renderCodeMap$ cleared it, and the blacklist actions that set it are not in actionsRequiringRerender, so a dispatch that changed nothing latched it true for the rest of the session. Add a backstop deadline that cannot outlive its own dispatch. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Move axisCard, sliderNumberInput, settingsPopoverShell and the settingsInput helpers out of metricsBar into features/shared so a second settings bar can compose them, and replace the metricsBar host chrome with a reusable BarShellDirective. Pure structural change: the vertical offset stays with each bar via the BAR_BOTTOM_* constants. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Load the cc.json 2.0 domain lens into a domainLensSource state home and project it through a pure domain lens, alongside a domainBar home for the word-cloud presentation settings. Merge domain words per file on load, re-keying paths the way edges are handled so multi-file mode aggregates correctly, and persist the domainBar settings via saveCcState. Also teach the ValidationTool schema and simplecc.sh about the domain lens, and carry the lens in both sample1.cc.json copies. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Lift the positioning and show/hide logic out of the codeMap tooltip service into a standalone hoverTooltip service so a non-3D view can reuse it. Behaviour is unchanged. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Split the single page into routed metrics and domain views, with a nav bar view switcher and a keep-alive route reuse strategy so switching back does not rebuild the map. Routing uses hash location and preserves query params across switches. The domain view renders the domain lens as an echarts word cloud with a domainBar for shape, rotation and sizing settings, built on the shared bar UI kit. The sidebar explorer is generalized behind an explorerHost so both views drive it, and the loading spinner becomes per-view via a viewReadiness store. Adds a lint:styles check enforcing Tailwind utilities over component style files, wired into the visualization CI workflow. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
HoverTooltipRow has no importers outside the service, so exporting it only widened the module's surface. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The nav bar sits above the router outlet and outlives every view switch, so it rendered one fixed set of trailing controls regardless of what was on screen — putting the map-only 3D Print and Explore/Compare on the domain view. Each view now declares the controls it wants in viewNavBarControls, and the nav bar renders from that declaration. The active view comes from the URL rather than view lifecycle, because the route-reuse strategy detaches views instead of destroying them. Each divider is the trailing separator of the control above it, so a hidden control takes its divider with it. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The layout silently left out every word it could not place, so large maps
showed far fewer words than the Words slider promised, with no indication.
- Fit all words now defaults to on, so words are shrunk rather than dropped.
- A new Draw outside bounds toggle lets words render past the layout edge;
the size-range fit is skipped when it is on, since nothing gets skipped.
- The cloud reports any remaining shortfall ("212 of 300 words fit …"),
counted from the graphic elements the layout hands out — no public echarts
API reports it.
- The Words slider goes up to 1000 (previously 300).
Renders also got slower with every interaction: echarts-wordcloud dispatches
its abort CustomEvent without cancelable: true, so the preventDefault()-based
cancel was a no-op and every superseded layout kept placing words in the
background. Patched via patch-package, which also disposes the previous
layout per chart when a new one starts. A render after 15 interrupted
layouts drops from 19.7 s to 1.3 s.
Resizes no longer run their own debounce calling chart.resize() directly —
that laid out once and then again for the re-fitted font clamp, the visible
double render. Words, settings and resizes now funnel through one debounce,
which measures height as well as width and skips a detached 0x0 view.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Slim down the DomainLanguageParser (drop WordAnalyzer) and reorganize the visualization domain feature: move the word cloud into the renderer, consolidate domainBar segments, and relocate view-readiness/domain state into the routing and store layers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ttled Excluding a file extension (or otherwise changing a settled map) gated its render on a staleness flag that a sibling effect only raised after the changed data had already passed through, so the render was skipped and the metrics spinner stayed up forever. Split the trigger: a genuine data/setting change always rebuilds the visible map, while the staleness check now guards only the switch-back-to-metrics path. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Extract the sidebar explorer into shared row/selection/sort/context-menu ports so the domain and metrics views can host it independently. Move formatCompactNumber to util, add per-view explorer sort state, and wire domain sorting selectors. Document the enforced layering in TARGET-ARCHITECTURE.md. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The save-trigger union registered only 7 of the 11 domain-state setters that are dispatched on restore, so drawOutOfBound, shrinkToFit, sortingOrder and sortingOrderAscending were restored on load but never written back — the per-view sort silently did not survive a reload. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Remove the Language.keywordFilter field and its 17 ResourceKeywords(...) arguments: it had zero production read sites, since real language filtering runs through ConfigurationBuilder, which loads the keyword resources itself. The keyword classes (LanguageKeywords/ResourceKeywords/ResourceKeywordLoader) stay — ConfigurationBuilder and PathScopedKeywordProvider use them directly. Remove the three production StopWordFilter methods with no production caller (filter(words), filter(words, filePath), isExcluded(word)); only the path-aware isExcluded(word, filePath) is used (FilterStage). Drop the duplicate weight validation from ExtractionWeights so weight positivity is asserted once, at the CLI boundary where the message can name the offending --*-weight option. To avoid discarding real coverage, the affected tests are routed through the surviving APIs rather than deleted: LanguageTest loads each keyword resource directly (keeping every keyword file referenced and its contents validated), and the StopWordFilter exclusion-algorithm tests call the retained path-aware method via small test-only helpers. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
V18 seeded the state root under the old "domainBar" key and V19 immediately renamed it to "domainState" — two migrations fighting over a key that never shipped in any released blob (main is at v16; v17-v19 are all new on this branch). Collapse them into a single V18 that seeds "domainState" directly, drop V19 and its registry entry, and set DB_VERSION = 18. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Replace the hand-rolled router.url.split("?")[0] === routeLinks.domain in
RedirectAwayFromDomainViewEffect with the existing viewIdForLink(...), so
URL-to-view parsing lives only in routePaths. Extract pathToNodeName(path,
fallback) into nodePathHelper and call it from both the word-cloud read store
and the domain view, which had each hand-rolled the leaf-name idiom with
different fallbacks.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Route the navBar, fileExtensionBar and bottomBar imports of the shared PublishesHeightDirective through the shared feature's public-API barrel instead of reaching into its internal path, clearing three dependency-cruiser feature-cross-feature-only-via-public-api violations. Also apply biome's organizeImports fixes that had slipped past the pre-commit hook. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
findApplicableFrameworks returned the frameworks of the FIRST enclosing directory found while iterating an unordered HashMap, so for a file nested under more than one framework directory (e.g. a monorepo with React at the root and Angular in a sub-package) which keywords applied depended on hash iteration order — nondeterministic across runs and silently dropping the other directory's keywords. Union the frameworks of every enclosing directory instead; the result no longer depends on iteration order. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- FrameworkDetector: extract the shared find-files/parse/identify/store skeleton and a walkFiles helper across the JS and C# ecosystems; both now store results through the same per-directory merge, fixing the inconsistency where the C# path overwrote (existing + frameworks) while the JS path merged. - Extract parseWordLines, shared by ResourceKeywordLoader and DlcIgnoreParser, so the keyword-file format lives in one place. - Use Map.merge for the count-accumulation idiom (TfIdfCalculator, DirectoryWordAggregator). - TestFileDetector: fold the identical Kotlin/Java TypeName+Test patterns into one helper and share matchesAnyExtension with FileFilter. - ConfigurationBuilder: inject FrameworkDetector and DlcIgnoreParser via the constructor. - Delete the zero-assert SilentProgressReporterTest (a null object with nothing to assert). - Trim redundant comments (AnalysisConfiguration section dividers, WordFrequency KDoc) and rename a single-letter loop index. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
- domainExplorerSelection.topWords now calls the exported selectTopWords instead of re-implementing the rank-by-sizing-value comparator, so the hover tooltip and the rendered cloud can no longer rank words differently. - Single-source the metrics-view explorer capabilities as DEFAULT_EXPLORER_CAPABILITIES, reused by the view provider and the port mock instead of two identical literals. - Extract seedRootIfAbsent from the near-identical v17/v18 IndexedDB seed migrations. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…ivider The domain view's cloud container fell back to a 28px bottom-bar height while every other consumer used 32px, so the two disagreed until the bottom bar published its measured height. Align on 32px and name the bar-height fallbacks (DEFAULT_BOTTOM_BAR_HEIGHT_PX, DEFAULT_FILE_EXTENSION_BAR_HEIGHT_PX) so the TS-side calc() strings share one source. Extract the thrice-repeated nav-bar hairline into a cc-nav-divider component. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DomainViewComponent owned a clipboard-copy-with-transient-feedback concern (navigator.clipboard, a copied signal, a feedback timeout and its DestroyRef teardown) that had nothing to do with composing the view. Move it into a component-scoped CopyToClipboardService with its own coverage, leaving the component to compose and delegate. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DEFAULT_BOTTOM_BAR_HEIGHT_PX and DEFAULT_FILE_EXTENSION_BAR_HEIGHT_PX are only referenced within barShell.directive.ts, so exporting them tripped knip's unused-export check (missed in the prior commit because only the dependency-cruiser line of the lint output was inspected). Make them module-private. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
SourceAnalyzer and DirectoryWordAggregator both built a WordFrequency and looked its TF-IDF score out of the scores map by hand. Add WordFrequency.withScore and call it from both so the score-attachment lives in one place. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…spec - Extract withRangeMin/withRangeMax so the rotation and word-sizing popovers stop hand-indexing the [min,max] tuple (a transposition risk when only one is edited). - Cross-reference the word-cloud brand colors between color.util.ts and tailwind.css so the duplicated fallback literals cannot silently drift. - Reduce metricsExplorerRow.spec to the adapter's wiring; the projection branches it re-tested are already covered by explorerRow.projection.spec. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Add a tree-find bar to the domain explorer (reveals/expands/scrolls to matches), a reusable toast that explains the domain->map redirect, and publish the explorer width so the floating settings bars center clear of the sidebar. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Readable shape labels (Circle/Heart/...), Title-Case toggle labels, 10px smallest-word floor, horizontal-dominant default rotation, a 'Metrics' switcher label, and keeping the selection when a folder is collapsed in the domain view (metrics view unchanged). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The fill step enlarged fonts toward containerWidth*0.2, and echarts-wordcloud layout cost scales with word area (worse with shrinkToFit), so a single layout could take seconds on wide windows. Keep the width-based shrink (never drops the largest word) and the horizontal rotation default; drop only the enlargement. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
DomainState gained sortingOrder/sortingOrderAscending, but several test mocks still built it from defaultWordCloudSettings, breaking tsc. Route them through defaultDomainState, preserving each test's intent. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Fold the domain view's own development churn (word sizing, settings bar areas, tooltip, spinner and layout regressions) into the feature entry it belongs to, since none of it ever reached a user. Record the Authors scenario and the Code Smells metric correction, which did have no entry, and describe the domain language parser's CLI surface. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
…roles The toast region and the word cloud's fit notice become <output>, the view switcher a labelled <nav>, the settings popover a <dialog>, and the cloud's text alternative a <figcaption> inside a <figure>. JSDOM loads no author stylesheet, so it would hide a closed <dialog> that the browser shows; the unit setup mirrors the app's own display declaration. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Raise the type library to ES2023 for it, and hoist the reveal service's default options out of the signature so each call shares one object. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
waitForFunction does not await a promise its predicate returns, and a promise object is truthy, so the IndexedDB read passed on the object alone — for a file name that could never exist it returned in 13ms. The restore test then reloaded while the debounced save was still pending and booted the sample files instead: 2 of 12 runs. Polling through page.evaluate, which does await, makes it 0 of 12. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The refresh test asked the persistence wait for "sample1 +1", the label the map selector shows for the boot pair, while the record keys files by "sample1.cc.json". The wait could never be satisfied — invisible until the wait started waiting for real. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
A session persisted before the domain lens existed restores file states whose fileSettings carry no domainWords, so the domain selectors crash on startup. Migration v19 seeds an empty word bank on every such file state. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The explorer leaked state between the map and domain views. Because KeepAliveRouteReuseStrategy detaches views instead of destroying them, a detached view never ran its cleanup, so bar heights published to documentElement went stale and root-provided services were shared by both trees. - publish bar heights and the explorer width onto the view host, so a detached view takes its variables out of the document with it. This fixes the gap between the explorer and the bottom bar in the domain view, which kept reserving room for the map's file-extension bar. - provide collapse, width and reveal per view, keyed per view in local storage with a fallback to the pre-split key. - add an EXPLORER_SEARCH port so each view owns its search pattern. The domain view's enter-to-cycle find bar is replaced by the same live search the map view uses, backed by a new domainState.searchPattern. - move the flatten/exclude actions into their own component, rendered only for views that own blacklist rules. - reveal-after-load became a host directive on the metrics view, so a load no longer scrolls the domain tree to the map's selection. - surface flattened state through the row lens instead of the node, and drop the area-metric dependency from non-area sort orders. - collapse the per-view sort and search adapters into config-driven provider factories, so a further view wires both ports in two lines. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
knip reported `_isSearchPatternEmpty` as an unused export of the sidebarExplorer facade. The underscore-prefixed predicate exists for the memoized selector and its spec, both of which import it directly; the only cross-feature consumer imports `isSearchPatternEmptySelector`. Publishing it on the facade widened the feature's public surface for no caller. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The sidebar explorer is shared by the map and domain views, so it must not read or write either view's state. Its tree, row projection, counts, rules, search and sort now arrive through per-view ports implemented under views/, which is what stops the domain explorer from inheriting the map's marked packages, blacklist and searched nodes. The domain explorer reads a view-state-independent tree from the structure lens, so excluded files neither disappear from it nor skew the file counts it sorts by, and the rule and count chips belonging to those rules are left out of it. The dependency-cruiser rule is widened to match: the feature is fenced off sharedView, mapState, domainState and renderModel entirely, not just off the sharedView write facade. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbtBcSZAFtiTv2yFkMBMJX
File controls stay on the left, the Metric | Domain tabs sit in the center, and only Settings remains on the right. Explore, Compare and 3D Print move out of the right-hand side into a bar that drops down over the map while a tab is hovered or focused, so the top bar itself no longer changes with the view. A handle below the center of the bar marks it as a drawer and pulls it open for the current view. 3D Print stays clickable from every view: outside the metric view it now asks whether to switch instead of sitting there disabled, and "Switch and continue" opens the metric view and goes straight to the export once the map is drawn. Moving the dialog state into a store lets the always-mounted nav bar host it, so it survives the mode bar that opened it. The domain tab also stays reachable in compare mode, leaving compare mode rather than bouncing back to the map. The e2e timeout moves to 60s so the suite's deliberate boot and persistence waits can run to completion instead of being cut off on a loaded machine. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbtBcSZAFtiTv2yFkMBMJX
Leaving the domain view detaches its DOM, which measures as a zero-sized container and measures back to the old size on return, replaying the whole layout animation for inputs that had not changed. The host now compares against what actually reached the chart rather than against a queued layout, and skips the re-render. The empty state destroys the container element, so the one handed back on return is a new element. The chart is disposed and reattached in that case, instead of drawing on a container nobody can see. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbtBcSZAFtiTv2yFkMBMJX
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbtBcSZAFtiTv2yFkMBMJX
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbtBcSZAFtiTv2yFkMBMJX
TreeSitter treats a byte order mark as source text and mis-tokenizes the identifiers around it, which extracted "class" as the two words "cla" and "ss". Files saved with a BOM therefore polluted the domain vocabulary with fragments. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbtBcSZAFtiTv2yFkMBMJX
carriesData() lived in the merge filter, but naming the opaque lenses that actually hold data is a statement about a LensSet, and a second filter needs the same answer. It moves to :model alongside LensSet, together with the LensSet.dataBearingOpaqueLensNames the guards actually want, so the next change can reuse it instead of copying it across modules. No behaviour change. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbtBcSZAFtiTv2yFkMBMJX
The domain lens is keyed by node id, a path hash, but --set-root, --move-from/--move-to and --remove passed the opaque lenses through verbatim while re-pathing or dropping nodes. Surviving nodes lost their words and removed ones left keys pointing at nothing, with no warning at all — the visualization then silently dropped whatever no longer resolved. These three actions now refuse, log why and exit non-zero, mirroring the guard `ccsh merge --large` already applies. --rename-mcc and --print-levels leave paths alone and still work on a file carrying the lens, as does modify on any file without one. Re-keying the lens instead would assume every opaque lens is a flat node-id map, which only holds for `domain` today; that is left for when a second one exists. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbtBcSZAFtiTv2yFkMBMJX
It was the only analyser the golden test did not exercise, so nothing checked that its output stays valid cc.json on every PR. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbtBcSZAFtiTv2yFkMBMJX
The demo maps carried no domain data, so the Domain tab never appeared on GitHub Pages or in the bundled maps and the view shipped invisible. The parser runs on the unmodified tree, clear of the `modify --set-root` step, whose re-pathing would invalidate the node ids the lens is keyed by. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbtBcSZAFtiTv2yFkMBMJX
Rust, Vue and ABL were missing from the parser page. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbtBcSZAFtiTv2yFkMBMJX
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbtBcSZAFtiTv2yFkMBMJX
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbtBcSZAFtiTv2yFkMBMJX
The domain lens was the only lens whose top level was the per-node map
itself, so it had nowhere to grow: metrics has `attributes`, dependency
has `edges`, clusters has `clusterings`. Adding corpus-wide data or a
per-node fact would have meant a breaking change once 2.0 ships.
"domain": { "nodes": { "<id>": { "words": [ ... ] } } }
This leaves two growth points: lens-level siblings to `nodes` and
per-node siblings to `words`.
`nodes` is optional rather than required, so an unused lens slot stays
`{}`. `carriesData()` is a shallow size check, so a mandatory envelope
key would make every empty domain lens data-bearing, turning merges of
the fixtures that carry `"domain": {}` into conflicts and blocking
StructureModifier re-paths.
The representative project in EveritValidatorTest now carries a domain
lens; the DomainLens, DomainNode and DomainWord schema definitions had
no analysis-side coverage before. A guard rejects the pre-envelope
shape.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GbtBcSZAFtiTv2yFkMBMJX
`check_domainlanguage` passed `${DATA}/sourcecode.java` to a parser that
scans directories, so it logged "Directory does not exist or is not a
directory", processed 0 files and emitted an empty lens. `validate`
accepts that, so the check only ever asserted an empty run produces a
valid file.
Scan `${DATA}` instead, and assert the lens carries the nodes envelope
and a word bank, the way check_convert asserts its output shape.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GbtBcSZAFtiTv2yFkMBMJX
Records why `nodes` stayed optional, the carriesData() sharp edge the envelope exposes, and that domain word banks should combine like NodeMaxAttributeMerger when the merge contract is built. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01GbtBcSZAFtiTv2yFkMBMJX
|
…anguageParser Move hardcoded dependency versions into the version catalog, type a read-only map view as Map instead of MutableMap, build a set without an intermediate mutable collection, and mark the single-method LanguageKeywords interface as fun interface. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
…ort dialog Use a semantic fieldset instead of a div with role="group" for the view-mode button group, and replace the fixed setTimeout guess for the map recolor with ThreeRendererService.afterRender$, which fires right after the actual repaint. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
…lens
Port the DomainLanguageCharta analyzer into ccsh as a first-class parser (
ccsh domainlanguageparser) that extracts domain vocabulary (word frequencies, optional n-grams, TF-IDF) from source code and writes it into the reserved cc.json 2.0domainlens, keyed by node id over the standard files tree.domainlens; keys computed with NodeId.fromSegments so they resolve against the ids the 2.0 writer emits (File leaves, Folder dirs, empty-segment root). tfidf omitted when null; deterministic, byte-stable output.Verified equivalent to the original DLC tool: same word/frequency/TF-IDF data on identical inputs; domain lens survives a mergefilter round-trip.
{Meaningful title}
Please read the CONTRIBUTING.md before opening a PR.
Closes: #
Description
Descriptive pull request text, answering:
Definition of Done
A PR is only ready for merge once all the following acceptance criteria are fulfilled:
Screenshots or gifs