Skip to content

Conversation

@pranavgaikwad
Copy link
Contributor

@pranavgaikwad pranavgaikwad commented Nov 19, 2025

This implements Prepare() function for nodejs provider too.

Prepare() function will be called prior to running Evaluate(). The providers will be passed all conditions in the Prepare() function where the providers may choose to perform custom logic after Init() but before Evaluate(). The generic provider uses the Prepare() call to query document symbols and cache them in-memory. Evaluate() call simply returns the cached results.

Summary by CodeRabbit

  • New Features

    • Providers gain a preparation step to accept preloaded condition data before rule execution.
    • Symbol caching and enhanced Node.js symbol search to improve detection and performance.
  • Updates

    • File-walking now honors exclusion rules to skip irrelevant folders and files.
    • Sample rules and test outputs expanded (new Node.js sample rule, updated line references, TSX/JSX coverage).
    • Demo environment now installs Node.js dependencies for examples.

✏️ Tip: You can customize this high-level summary in your review settings.

@coderabbitai
Copy link

coderabbitai bot commented Nov 19, 2025

Walkthrough

The PR adds a provider preparation phase: rule loading now extracts per-provider conditions, the analyzer accumulates them, and invokes a new Prepare(ctx, conditions) method on each provider before engine initialization. Supporting changes include symbol caching, a Node.js-specific symbol search helper, file-exclusion-aware directory walking, and proto/GRPC updates for Prepare/NotifyFileChanges.

Changes

Cohort / File(s) Summary
Main Orchestration
cmd/analyzer/main.go
Thread provider-specific conditions from rule loading into a global map and call each provider's Prepare(ctx, conditions) after ProviderInit and before engine setup.
Rule Parser API
parser/rule_parser.go, parser/rule_parser_test.go
LoadRules/LoadRule/getConditions now return map[string][]provider.ConditionsByCap; added mergeProviderConditions; tests updated to accept extra return value and test provider Prepare stub added.
Provider Core API & Helpers
provider/provider.go, provider/provider_test.go, provider/lib.go
Added ConditionsByCap type and ServiceClient.Prepare method; added FullPrepareResponse orchestration; updated file-walk APIs to accept excludedDirs/excludedPatterns and skip matches; cachedWalkDir signature changed.
gRPC API & Server
provider/internal/grpc/library.proto, provider/server.go
Added Prepare RPC and messages (ConditionsByCapability, PrepareRequest, PrepareResponse); added id to NotifyFileChangesRequest; server handlers for Prepare and NotifyFileChanges implemented.
gRPC Provider & Client
provider/grpc/provider.go, provider/grpc/service_client.go
grpcProvider.Prepare added delegating to FullPrepareResponse; grpcServiceClient.Prepare implemented (marshalling, RPC, error handling); NotifyFileChanges includes Saved and Id.
Builtin Provider Implementation
provider/internal/builtin/provider.go, provider/internal/builtin/service_client.go
Added no-op Prepare implementations (placeholders/TODO).
External Providers — Prepare Stubs
external-providers/.../*/provider.go, .../service_client.go (dotnet, java, yq, generic, etc.)
Added Prepare no-op methods across provider and service client types to satisfy new interface; minor Init arity adjustments in some generic builders.
LSP Base: Symbol Cache & Interface
lsp/base_service_client/base_service_client.go, lsp/base_service_client/symbol_cache.go, lsp/base_service_client/base_capabilities.go
Introduced SymbolSearchHelper interface and injected helper into NewLSPServiceClientBase; added thread-safe SymbolCache and default symbol search helper; changed GetAllDeclarations signature and embedded provider.ProviderContext into ReferencedCondition.
Node.js Symbol Helper & Client
external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go, .../nodejs/service_client.go
Added Node.js-specific nodejsSymbolSearchHelper and NewNodejsSymbolCacheHelper; nodejs service client refactored to use symbol helper, removed per-file open/close traversal, adjusted incident mapping (CodeLocation/FileURI) and reference filtering; added ProviderContext in referenced conditions.
Generic External Provider Updates
external-providers/generic-external-provider/go.mod, .../generic/service_client.go, .../pylsp/service_client.go, .../yaml_language_server/service_client.go
Updated analyzer-lsp dependency; added indirect dependency; adjusted NewLSPServiceClientBase calls to pass extra nil argument where required.
Examples & Demo
examples/nodejs/package.json, demo-output.yaml, demo-local.Dockerfile
Added Node.js package.json; updated demo output with new/adjusted sample rule incidents; Dockerfile runs npm install for examples.

Sequence Diagram(s)

sequenceDiagram
    participant Main as Analyzer Main
    participant Parser as Rule Parser
    participant ProvMgr as Provider Manager
    participant Provider as Provider (each)
    participant Engine as Rule Engine

    Main->>Parser: LoadRules(filepath)
    activate Parser
    Parser-->>Main: (ruleSets, clients, provConditions, err)
    deactivate Parser

    Main->>ProvMgr: ProviderInit(clients)
    ProvMgr-->>Main: initialized clients

    rect rgb(230,245,255)
    Note over Main,Provider: NEW — Provider Prepare Phase
    Main->>Main: aggregate provConditions per provider
    loop for each provider
        Main->>Provider: Prepare(ctx, conditions)
        alt prepare success
            Provider-->>Main: nil
        else prepare error
            Provider-->>Main: error (logged, continue)
        end
    end
    end

    Main->>Engine: Initialize(ruleSets)
    Engine-->>Main: ready
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Areas to focus review on:

  • lsp/base_service_client symbol cache, SymbolSearchHelper interface, and GetAllDeclarations signature change.
  • Node.js symbol helper parsing/matching logic and its integration with the nodejs service client.
  • provider/lib.go changes to cachedWalkDir and exclusion-aware walking.
  • End-to-end flow for collecting, merging, marshalling, and delivering provider conditions (parser → main → provider.Prepare → gRPC).

Possibly related PRs

Suggested reviewers

  • shawn-hurley
  • eemcmullan
  • jmle

Poem

🐇 I hopped through rules and cached each name,

Packed conditions by cap into a tidy frame.
Providers prepped as dawn met code,
Symbols found in every node.
Hop! The engine wakes — analyze, proclaim!

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.54% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly and concisely summarizes the main change: adding a Prepare() function to providers for pre-evaluation warmup, which aligns with the core objective of implementing pre-evaluation preparation logic.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

…nces, query document symbols

Signed-off-by: Pranav Gaikwad <[email protected]>
Signed-off-by: Pranav Gaikwad <[email protected]>
Signed-off-by: Pranav Gaikwad <[email protected]>
Signed-off-by: Pranav Gaikwad <[email protected]>
Signed-off-by: Pranav Gaikwad <[email protected]>
@pranavgaikwad pranavgaikwad force-pushed the feature/preEvaluationWarmup branch from b67a85a to 60c19c7 Compare November 20, 2025 20:09
variables:
file: file:///examples/nodejs/test_b.ts
effort: 1
node-sample-rule-003:
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This is correctly matching now for query greeter matching on greeter.hello()

lineNumber: 1
variables:
file: file:///examples/nodejs/Component.tsx
- uri: file:///examples/nodejs/LegacyComponent.jsx
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Same here

@pranavgaikwad pranavgaikwad marked this pull request as ready for review November 21, 2025 01:15
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
parser/rule_parser.go (1)

615-784: getConditions: providerConditions is collected but never returned or merged

In getConditions you:

  • Introduce providerConditions := map[string][]provider.ConditionsByCap{} (Line 618).
  • Call mergeProviderConditions when encountering a provider condition (Lines 750-753).
  • Still return only ([]engine.ConditionEntry, map[string]provider.InternalProviderClient, error) and ignore providerConditions in the final return conditions, providers, nil (Line 783).

As a result:

  • Work done in mergeProviderConditions here is effectively dead; only logs are observable.
  • Nested and/or conditions never contribute to the providerConditions map returned by LoadRule, so providers miss those conditions during Prepare.

Consider one of the following:

  1. Propagate nested providerConditions (strongly recommended):

    • Change the signature to include the map:
      func (r *RuleParser) getConditions(
          conditionsInterface []interface{},
      ) ([]engine.ConditionEntry, map[string]provider.InternalProviderClient, map[string][]provider.ConditionsByCap, error)
    • Return providerConditions from getConditions.
    • In LoadRule’s and/or branches, capture and merge:
      conds, provs, nestedProvConds, err := r.getConditions(iConditions)
      // merge nestedProvConds into this function’s providerConditions map

    This gives providers a complete picture of conditions, regardless of nesting.

  2. Or, if nested conditions are intentionally ignored for Prepare:

    • Remove providerConditions from getConditions entirely (the local map and mergeProviderConditions calls), to avoid confusion about unused state.

Right now it’s in an in‑between state that’s easy to misinterpret and doesn’t fully support the new Prepare behavior.

🧹 Nitpick comments (13)
provider/lib.go (2)

57-63: Exclude precedence wiring looks correct but split uses raw paths

Using rule-level excludes when present and otherwise falling back to provider-level is consistent with the existing priority model used later in the function for filtering. The only caveat is that splitPathsAndPatterns here operates on the raw constraint strings (without joining to BasePath), so relative defaults like "node_modules" will only be classified as directories if they exist relative to the process working directory, not the workspace base. That just affects how much the walker can prune early; final exclusion filtering still uses the full patterns and remains correct.

If you want directory pruning to work reliably for relative excludes, consider normalizing them against f.BasePath before/inside splitPathsAndPatterns in this call site.


285-335: Normalize absolute excludedDirs and precompile exclude patterns for WalkDir

The new cachedWalkDir implementation is generally sound, but there are a couple of edge cases and perf considerations worth tightening:

  • excludedDirs vs relPath:

    • Inside the walker you compute relPath := filepath.Rel(basePath, path) and compare it directly to each excludedDir.
    • GetExcludedDirsFromConfig (lines 439-483) constructs absolute paths for user-specified excludes, so when those end up in ExcludePathsOrPatternsexcludedDirs, the relPath == excludedDir || strings.HasPrefix(relPath, excludedDir) check will never fire for those entries. Those directories will still be excluded later by filterFilesByPathsOrPatterns, but they won’t be pruned during traversal.
    • Normalizing excludedDirs relative to basePath before the walk would make both default relative entries (e.g. "node_modules") and absolute config entries work consistently at traversal time.
  • Regex compilation inside the callback:

    • regexp.Compile(excludedPattern) is executed on every directory visit for every pattern; this is unnecessarily expensive on large trees.
    • Precompiling the subset of patterns you want to treat as regex once per basePath keeps the callback lightweight.
  • Cache key ignores excludes:

    • Today newCachedWalkDir is only used inside Search, and excludedDirs/excludedPatterns are constant for all calls within a single Search invocation, so caching solely on basePath is safe.
    • If this helper gets re-used with varying exclude sets for the same basePath, the cache will return results computed with the first exclude set. It’s worth documenting this assumption or including the exclude slices in the cache key if you expect broader reuse.

A possible refactor for the walker normalization and regex precompilation (keeps behavior but makes excludes effective for absolute dirs and more efficient):

 type cachedWalkDir func(path string, excludedDirs []string, excludedPatterns []string) ([]string, error)

 func newCachedWalkDir() cachedWalkDir {
   cache := make(map[string]struct {
     files []string
     err   error
   })
-  return func(basePath string, excludedDirs []string, excludedPatterns []string) ([]string, error) {
+  return func(basePath string, excludedDirs []string, excludedPatterns []string) ([]string, error) {
     val, ok := cache[basePath]
     if !ok {
       files := []string{}
-      err := filepath.WalkDir(basePath, func(path string, d fs.DirEntry, err error) error {
+      // Normalize excludedDirs relative to basePath so absolute config entries are effective.
+      relExcludedDirs := make([]string, 0, len(excludedDirs))
+      for _, ed := range excludedDirs {
+        if filepath.IsAbs(ed) {
+          if rel, err := filepath.Rel(basePath, ed); err == nil {
+            relExcludedDirs = append(relExcludedDirs, rel)
+            continue
+          }
+        }
+        relExcludedDirs = append(relExcludedDirs, ed)
+      }
+
+      // Precompile excludedPatterns we want to treat as regex.
+      var excludedRegexps []*regexp.Regexp
+      for _, p := range excludedPatterns {
+        if !strings.Contains(p, "*") {
+          continue
+        }
+        if rx, err := regexp.Compile(p); err == nil {
+          excludedRegexps = append(excludedRegexps, rx)
+        }
+      }
+
+      err := filepath.WalkDir(basePath, func(path string, d fs.DirEntry, err error) error {
         if err != nil {
           return err
         }
         if !d.IsDir() {
           files = append(files, path)
           return nil
         }
-        // do not recurse into excluded dirs
-        for _, excludedDir := range excludedDirs {
-          relPath, err := filepath.Rel(basePath, path)
-          if err == nil && (relPath == excludedDir || strings.HasPrefix(relPath, excludedDir)) {
-            return fs.SkipDir
-          }
-        }
-        for _, excludedPattern := range excludedPatterns {
-          if !strings.Contains(excludedPattern, "*") {
-            continue
-          }
-          regex, err := regexp.Compile(excludedPattern)
-          if err != nil {
-            continue
-          }
-          if regex.MatchString(path) || regex.MatchString(filepath.Base(path)) {
-            return fs.SkipDir
-          }
-        }
+        // do not recurse into excluded dirs
+        relPath, err := filepath.Rel(basePath, path)
+        if err != nil {
+          return err
+        }
+        for _, ed := range relExcludedDirs {
+          if relPath == ed || strings.HasPrefix(relPath, ed) {
+            return fs.SkipDir
+          }
+        }
+        for _, rx := range excludedRegexps {
+          if rx.MatchString(path) || rx.MatchString(filepath.Base(path)) {
+            return fs.SkipDir
+          }
+        }
         return nil
       })
       cache[basePath] = struct {
         files []string
         err   error
       }{
         files: files,
         err:   err,
       }
       return files, err
     }
     return val.files, val.err
   }
 }

This keeps the public surface the same while making configuration-driven excludes more effective at the traversal stage and reducing per-entry overhead inside the walker.

Also applies to: 439-483

lsp/base_service_client/symbol_cache.go (1)

225-265: Minor readability issue: reused condition identifier in nested loops

In both MatchFileContentByConditions and MatchSymbolByConditions, the inner loop reuses the condition identifier from the outer loop:

for _, condition := range conditionsByCap {
    if condition.Cap == "referenced" {
        for _, condition := range condition.Conditions {
            // ...
        }
    }
}

This shadowing doesn’t break correctness but makes the code harder to read and reason about. Renaming the inner variable (e.g., to rawCond or data) would improve clarity with no behavior change.

provider/grpc/service_client.go (1)

201-225: Prepare RPC mapping looks correct; consider minor allocation tweaks

The mapping from []provider.ConditionsByCap to []*pb.ConditionsByCapability and the error handling around Prepare are consistent with the existing Evaluate path and look correct.

You could shave a couple of allocations by pre-allocating the slices, e.g.:

 func (g *grpcServiceClient) Prepare(ctx context.Context, conditionsByCap []provider.ConditionsByCap) error {
-	conditionsByCapability := []*pb.ConditionsByCapability{}
-	for _, condition := range conditionsByCap {
-		conditions := []string{}
+	conditionsByCapability := make([]*pb.ConditionsByCapability, 0, len(conditionsByCap))
+	for _, condition := range conditionsByCap {
+		conditions := make([]string, 0, len(condition.Conditions))
 		for _, conditionInfo := range condition.Conditions {
 			conditions = append(conditions, string(conditionInfo))
 		}

Not required for correctness, just a small optimization if this runs on many rules.

parser/rule_parser_test.go (1)

23-26: Test provider now satisfies Prepare; consider exercising provider-conditions in tests

The no-op Prepare on testProvider correctly satisfies the updated InternalProviderClient interface, and updating the LoadRules call to ruleSets, clients, _, err := ... keeps existing assertions working while ignoring the new return value.

To get coverage of the new provider-conditions / preparation path, you might later add a focused test that:

  • Captures the third return from LoadRules, and
  • Uses a testProvider that records conditionsByCap passed to Prepare, so you can assert that the parser builds and wires conditions as expected.

Not required for this PR, but would lock in the new behavior.

Also applies to: 828-828

provider/server.go (1)

532-579: Prepare/NotifyFileChanges RPC wiring looks correct; consider guarding missing client IDs

The conversions from gRPC types to ConditionsByCap and FileChange and delegation to client.client.Prepare / NotifyFileChanges are consistent with the rest of the server and look fine.

One potential improvement: client := s.clients[in.Id] silently returns the zero value if the ID is unknown, which will panic on client.client.Prepare/NotifyFileChanges. For robustness (and nicer errors if the client was already stopped), consider checking the ok from the map lookup and returning a gRPC error when the client is not found.

cmd/analyzer/main.go (1)

293-350: Provider conditions aggregation and Prepare orchestration look good; clarify error handling semantics

The new providerConditions map populated from provConditions and the subsequent loop:

  • Aggregates per-provider []provider.ConditionsByCap correctly by appending v....
  • Only calls Prepare(ctx, conditions) for providers actually present in needProviders, which aligns with rules-driven usage.
  • Invokes Prepare after all ProviderInit calls (including builtin), matching the intended lifecycle.

Two minor points to consider:

  1. Duplicate conditions: If the same rule file is loaded multiple times or multiple rules contribute identical conditions for a provider/capability, you will send duplicates to Prepare. If this is undesirable, you might want to deduplicate per (provider, cap, condition) before calling Prepare.

  2. Non-fatal Prepare errors: Right now, Prepare failures are only logged. If some providers depend heavily on warmup (e.g., Node.js symbol cache), you may want to make failures for those providers fatal (or at least expose them via progress reporting) so callers understand why later evaluations might be incomplete.

external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go (1)

29-73: GetDocumentUris is functionally correct; consider leveraging conditions and normalizing base path

GetDocumentUris correctly:

  • Normalizes config.Location by stripping file:// when present.
  • Collects additional roots from workspaceFolders in ProviderSpecificConfig.
  • Uses FileSearcher with JS/TS includes and sensible default excludes (git, dist, vscode, husky, node_modules).
  • Returns file:// URIs via uri.File(file).

Two minor improvements you might consider:

  1. Use primaryPath for excludes as well

If InitConfig.Location is ever a file:// URI, filepath.Join(h.config.Location, ".git") etc. will not match real filesystem paths. Using the normalized primaryPath would be more robust:

-           ExcludePathsOrPatterns: []string{
-               filepath.Join(h.config.Location, ".git"),
-               filepath.Join(h.config.Location, "dist"),
-               filepath.Join(h.config.Location, ".vscode"),
-               filepath.Join(h.config.Location, ".husky"),
-               ".*node_modules.*",
-           },
+           ExcludePathsOrPatterns: []string{
+               filepath.Join(primaryPath, ".git"),
+               filepath.Join(primaryPath, "dist"),
+               filepath.Join(primaryPath, ".vscode"),
+               filepath.Join(primaryPath, ".husky"),
+               ".*node_modules.*",
+           },
  1. Optionally narrow the search using rule conditions

The conditionsByCap ...provider.ConditionsByCap parameter is currently unused. If performance becomes an issue on very large workspaces, you could parse the referenced patterns here (via parseQuery) to selectively add search roots (e.g., node_modules/@pkg/scope) instead of always scanning all source directories.

lsp/base_service_client/base_service_client.go (2)

120-158: Symbol cache integration and SymbolSearchHelper wiring look solid; be mindful of WaitGroup semantics

The additions to LSPServiceClientBase:

  • symbolCache, symbolSearchHelper, symbolCacheUpdateChan, symbolCacheUpdateWaitGroup, and allConditions.
  • SymbolSearchHelper interface and its injection via NewLSPServiceClientBase.
  • Initialization of default helper when none is passed, plus background symbolCacheUpdateHandler.

are all consistent and keep the base client generic while enabling Node.js (and other languages) to customize symbol discovery and matching.

A couple of points to keep in mind:

  1. WaitGroup used across Prepare/NotifyFileChanges and GetAllDeclarations

Prepare and NotifyFileChanges both call symbolCacheUpdateWaitGroup.Add(...), while GetAllDeclarations calls symbolCacheUpdateWaitGroup.Wait() before reading from the cache. This guarantees that any currently queued updates complete before GetAllDeclarations falls back to the cache, but:

  • New updates scheduled after Wait() begins will not be waited on, so a caller cannot rely on GetAllDeclarations to always see a fully up‑to‑date cache.
  • Add and Wait can run concurrently, which is discouraged in the sync.WaitGroup docs and can make reasoning about ordering harder.

If you need strong guarantees that GetAllDeclarations only sees a fully prepared cache (for the last Prepare), consider either:

  • Using a separate WaitGroup per Prepare invocation and waiting inside the orchestration layer (e.g., in the analyzer main) instead of inside GetAllDeclarations, or
  • Guarding Add and Wait with a dedicated mutex so they never run concurrently, keeping updates and reads well ordered.
  1. SymbolSearchHelper lifetime

Because NewLSPServiceClientBase always assigns a symbolSearchHelper (either injected or default) before starting the cache goroutine, all internal uses (GetLanguageID, MatchFileContentByConditions, etc.) are safe. Just ensure any future call sites outside this constructor also follow the “helper or default” pattern.

Overall, the wiring and caching strategy look good; the main thing is being explicit about the ordering guarantees you need from the WaitGroup and adjusting if stronger guarantees are required.

Also applies to: 142-152, 160-186, 187-196, 257-266, 314-348


314-333: Document symbol cache population pipeline is coherent; small robustness nits

The new document-symbol pipeline (NotifyFileChanges, populateDocumentSymbolCache, symbolCacheUpdateHandler, queryDocumentSymbol, searchContentForWorkspaceSymbols, getDefinitionForPosition, and helpers) is well thought out:

  • File changes invalidate per‑file cache entries and enqueue URIs for refresh.
  • populateDocumentSymbolCache:
    • Reads file content.
    • Uses searchContentForWorkspaceSymbols to find rule matches.
    • For each match, resolves definitions via textDocument/definition and builds WorkspaceSymbolDefinitionsPair values keyed by location.
  • queryDocumentSymbol:
    • Properly opens/closes documents via didOpen/didClose and caches DocumentSymbols by URI.
  • searchContentForWorkspaceSymbols:
    • Uses per‑line scanning and MatchFileContentByConditions to locate matches, then finds the nearest DocumentSymbol or synthesizes a fallback symbol.

A few minor robustness suggestions:

  1. Content vs on‑disk state

Both populateDocumentSymbolCache and getDefinitionForPosition read files from disk (os.ReadFile(uri.Filename())). If NotifyFileChanges is ever called with unsaved content (i.e., editor buffer not yet flushed), the cache will lag slightly behind the in‑memory state. If that matters for your use cases, consider optionally using provider.FileChange.Content when Saved == false instead of re‑reading from disk in NotifyFileChanges/populateDocumentSymbolCache.

  1. Scanner line limits

bufio.Scanner has a default token limit (~64K). If Node.js projects include extremely long lines, searchContentForWorkspaceSymbols may silently drop content. You can bump the buffer size with scanner.Buffer(make([]byte, 0, 64*1024), maxTokenSize) if this proves to be an issue.

  1. Graceful degredation on symbolSearchHelper errors

You already log and continue when didOpen / textDocument/documentSymbol / didClose fail. That’s good. Just ensure that symbolSearchHelper.GetLanguageID implementations never panic on unknown URIs; if needed, they should fall back to a default language ID.

These are all non-blocking; the overall design for symbol cache population and use is sound.

Also applies to: 479-577, 590-620, 622-677, 679-746, 748-773, 775-846

parser/rule_parser.go (3)

91-192: LoadRules: providerConditions aggregation looks correct but merge blocks are duplicated

The new providerConditions return and its aggregation across recursive LoadRules calls is consistent with how clientMap is handled and should behave correctly for both single-file and directory trees. The two for k, v := range provConditions blocks (directory vs. regular file cases) are effectively identical, though; consider extracting a tiny helper like mergeProvConditions(dst, src) to avoid drift if this logic changes again.


194-492: LoadRule: providerConditions only capture top-level provider conditions

The providerConditions map here is only updated in the default branch (direct {provider}.{capability} in the top-level when), so any provider references that live inside and/or clauses are not reflected in the returned providerConditions. Given this PR’s goal (pre‑evaluation warmup per provider), that means Prepare will miss some conditions and only see a subset of the rules’ provider usage.

If this is intentional (e.g., you only care about simple top‑level conditions for warmup), it would be good to document that assumption; otherwise, you’ll likely want to also aggregate conditions coming from nested and/or structures via getConditions (see next comment), so providers get a complete view of their usage.


871-892: mergeProviderConditions: behavior is fine, consider coalescing by capability

The helper correctly:

  • Serializes {capability: value} to YAML.
  • Appends a provider.ConditionsByCap entry under providerConditions[providerKey].

Two minor points to consider:

  • Multiple conditions for the same providerKey + capability will produce multiple ConditionsByCap entries with identical Cap values, each wrapping a single [][]byte. If providers would find it simpler to process one entry per capability, you could instead detect an existing ConditionsByCap for that cap and append to its Conditions slice.
  • YAML marshal failures are logged and swallowed, which is acceptable, but if missing condition info in Prepare would be problematic, you may want to surface that as an error rather than only logging it.

These are non‑blocking and mostly about shaping the data in a provider‑friendly way.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 616f4d8 and b068b33.

⛔ Files ignored due to path filters (4)
  • examples/nodejs/package-lock.json is excluded by !**/package-lock.json
  • external-providers/generic-external-provider/go.sum is excluded by !**/*.sum
  • provider/internal/grpc/library.pb.go is excluded by !**/*.pb.go
  • provider/internal/grpc/library_grpc.pb.go is excluded by !**/*.pb.go
📒 Files selected for processing (31)
  • cmd/analyzer/main.go (2 hunks)
  • demo-local.Dockerfile (1 hunks)
  • demo-output.yaml (3 hunks)
  • examples/nodejs/package.json (1 hunks)
  • external-providers/dotnet-external-provider/pkg/dotnet/provider.go (1 hunks)
  • external-providers/dotnet-external-provider/pkg/dotnet/service_client.go (1 hunks)
  • external-providers/generic-external-provider/go.mod (2 hunks)
  • external-providers/generic-external-provider/pkg/server_configurations/generic/service_client.go (1 hunks)
  • external-providers/generic-external-provider/pkg/server_configurations/nodejs/service_client.go (5 hunks)
  • external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go (1 hunks)
  • external-providers/generic-external-provider/pkg/server_configurations/pylsp/service_client.go (1 hunks)
  • external-providers/generic-external-provider/pkg/server_configurations/yaml_language_server/service_client.go (1 hunks)
  • external-providers/java-external-provider/pkg/java_external_provider/provider.go (1 hunks)
  • external-providers/java-external-provider/pkg/java_external_provider/service_client.go (1 hunks)
  • external-providers/yq-external-provider/pkg/yq_provider/provider.go (1 hunks)
  • external-providers/yq-external-provider/pkg/yq_provider/service_client.go (1 hunks)
  • jsonrpc2_v2/conn.go (2 hunks)
  • lsp/base_service_client/base_capabilities.go (2 hunks)
  • lsp/base_service_client/base_service_client.go (5 hunks)
  • lsp/base_service_client/symbol_cache.go (1 hunks)
  • parser/rule_parser.go (27 hunks)
  • parser/rule_parser_test.go (2 hunks)
  • provider/grpc/provider.go (1 hunks)
  • provider/grpc/service_client.go (1 hunks)
  • provider/internal/builtin/provider.go (1 hunks)
  • provider/internal/builtin/service_client.go (1 hunks)
  • provider/internal/grpc/library.proto (2 hunks)
  • provider/lib.go (8 hunks)
  • provider/provider.go (2 hunks)
  • provider/provider_test.go (1 hunks)
  • provider/server.go (1 hunks)
🧰 Additional context used
🧠 Learnings (3)
📚 Learning: 2025-10-21T16:27:59.216Z
Learnt from: shawn-hurley
Repo: konveyor/analyzer-lsp PR: 860
File: provider/server.go:251-259
Timestamp: 2025-10-21T16:27:59.216Z
Learning: In provider/server.go, the Init method uses context.Background() for creating the RPC client via GetProviderRPCClient because this RPC client is stored in the InitConfig and used across multiple subsequent requests (Evaluate, GetDependencies, etc.). The RPC client's lifecycle is managed separately through explicit Stop calls, not tied to the Init request's context.

Applied to files:

  • external-providers/generic-external-provider/pkg/server_configurations/pylsp/service_client.go
  • provider/grpc/provider.go
  • provider/grpc/service_client.go
  • external-providers/generic-external-provider/pkg/server_configurations/generic/service_client.go
  • external-providers/generic-external-provider/pkg/server_configurations/nodejs/service_client.go
📚 Learning: 2025-10-21T16:25:40.903Z
Learnt from: shawn-hurley
Repo: konveyor/analyzer-lsp PR: 860
File: lsp/base_service_client/base_service_client.go:212-214
Timestamp: 2025-10-21T16:25:40.903Z
Learning: In `lsp/base_service_client/base_service_client.go`, when an RPC connection is provided via `c.RPC` (not nil), it is treated as already initialized and no handler setup is performed. This is known technical debt that needs to be addressed in the future to ensure LSP notifications like `textDocument/publishDiagnostics` are properly processed.

Applied to files:

  • external-providers/generic-external-provider/pkg/server_configurations/pylsp/service_client.go
  • external-providers/generic-external-provider/pkg/server_configurations/generic/service_client.go
  • external-providers/generic-external-provider/pkg/server_configurations/nodejs/service_client.go
  • lsp/base_service_client/base_service_client.go
📚 Learning: 2025-07-30T12:11:45.673Z
Learnt from: pranavgaikwad
Repo: konveyor/analyzer-lsp PR: 859
File: external-providers/java-external-provider/pkg/java_external_provider/dependency.go:694-694
Timestamp: 2025-07-30T12:11:45.673Z
Learning: In the Java external provider dependency walker (external-providers/java-external-provider/pkg/java_external_provider/dependency.go), errors from toDependency function calls should be ignored as they are not considered important by the maintainers.

Applied to files:

  • external-providers/java-external-provider/pkg/java_external_provider/provider.go
🧬 Code graph analysis (20)
external-providers/java-external-provider/pkg/java_external_provider/service_client.go (1)
provider/provider.go (1)
  • ConditionsByCap (472-475)
external-providers/dotnet-external-provider/pkg/dotnet/provider.go (1)
provider/provider.go (1)
  • ConditionsByCap (472-475)
external-providers/dotnet-external-provider/pkg/dotnet/service_client.go (1)
provider/provider.go (1)
  • ConditionsByCap (472-475)
provider/internal/builtin/service_client.go (1)
provider/provider.go (1)
  • ConditionsByCap (472-475)
external-providers/yq-external-provider/pkg/yq_provider/service_client.go (1)
provider/provider.go (1)
  • ConditionsByCap (472-475)
provider/grpc/provider.go (1)
provider/provider.go (2)
  • ConditionsByCap (472-475)
  • FullPrepareResponse (433-441)
provider/provider_test.go (1)
provider/provider.go (1)
  • ConditionsByCap (472-475)
provider/grpc/service_client.go (2)
provider/provider.go (1)
  • ConditionsByCap (472-475)
provider/internal/grpc/library.pb.go (6)
  • ConditionsByCapability (1642-1648)
  • ConditionsByCapability (1661-1661)
  • ConditionsByCapability (1676-1678)
  • PrepareRequest (1694-1700)
  • PrepareRequest (1713-1713)
  • PrepareRequest (1728-1730)
provider/internal/builtin/provider.go (1)
provider/provider.go (1)
  • ConditionsByCap (472-475)
parser/rule_parser_test.go (1)
provider/provider.go (1)
  • ConditionsByCap (472-475)
external-providers/generic-external-provider/pkg/server_configurations/nodejs/service_client.go (3)
provider/provider.go (5)
  • Config (88-98)
  • ProviderContext (349-354)
  • Location (319-322)
  • IncidentContext (309-317)
  • Position (324-342)
external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go (1)
  • NewNodejsSymbolCacheHelper (173-179)
lsp/base_service_client/base_service_client.go (2)
  • LSPServiceClientBase (120-158)
  • NewLspServiceClientEvaluator (77-94)
lsp/base_service_client/base_capabilities.go (1)
provider/provider.go (1)
  • ProviderContext (349-354)
external-providers/java-external-provider/pkg/java_external_provider/provider.go (1)
provider/provider.go (1)
  • ConditionsByCap (472-475)
cmd/analyzer/main.go (1)
provider/provider.go (1)
  • ConditionsByCap (472-475)
external-providers/yq-external-provider/pkg/yq_provider/provider.go (1)
provider/provider.go (1)
  • ConditionsByCap (472-475)
external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go (5)
lsp/base_service_client/base_service_client.go (1)
  • SymbolSearchHelper (165-185)
provider/provider.go (2)
  • InitConfig (123-159)
  • ConditionsByCap (472-475)
provider/lib.go (3)
  • FileSearcher (19-28)
  • IncludeExcludeConstraints (38-41)
  • SearchCriteria (33-36)
lsp/protocol/tsprotocol.go (2)
  • Pattern (3424-3424)
  • WorkspaceSymbol (5052-5063)
lsp/base_service_client/symbol_cache.go (2)
  • WorkspaceSymbolDefinitionsPair (27-30)
  • NewDefaultSymbolCacheHelper (267-272)
lsp/base_service_client/base_service_client.go (3)
lsp/base_service_client/symbol_cache.go (4)
  • SymbolCache (20-25)
  • WorkspaceSymbolDefinitionsPair (27-30)
  • NewDocumentSymbolCache (32-37)
  • NewDefaultSymbolCacheHelper (267-272)
lsp/protocol/tsprotocol.go (20)
  • URI (4673-4673)
  • Log (5479-5479)
  • WorkspaceSymbol (5052-5063)
  • WorkspaceSymbolParams (5095-5101)
  • Location (2389-2392)
  • Range (3559-3564)
  • Info (5477-5477)
  • OrPLocation_workspace_symbol (2764-2766)
  • DocumentURI (1460-1460)
  • BaseSymbolInformation (51-65)
  • DidOpenTextDocumentParams (1100-1103)
  • TextDocumentItem (4485-4495)
  • Text (5341-5341)
  • DidCloseTextDocumentParams (1083-1086)
  • TextDocumentIdentifier (4478-4481)
  • DocumentSymbol (1388-1413)
  • DocumentSymbolParams (1448-1453)
  • File (5582-5582)
  • Position (3452-3466)
  • TextDocumentPositionParams (4499-4504)
provider/provider.go (5)
  • ConditionsByCap (472-475)
  • InitConfig (123-159)
  • FileChange (466-470)
  • Location (319-322)
  • Position (324-342)
provider/server.go (2)
provider/internal/grpc/library.pb.go (15)
  • PrepareRequest (1694-1700)
  • PrepareRequest (1713-1713)
  • PrepareRequest (1728-1730)
  • PrepareResponse (1746-1751)
  • PrepareResponse (1764-1764)
  • PrepareResponse (1779-1781)
  • NotifyFileChangesRequest (1546-1552)
  • NotifyFileChangesRequest (1565-1565)
  • NotifyFileChangesRequest (1580-1582)
  • NotifyFileChangesResponse (1598-1603)
  • NotifyFileChangesResponse (1616-1616)
  • NotifyFileChangesResponse (1631-1633)
  • FileChange (1486-1493)
  • FileChange (1506-1506)
  • FileChange (1521-1523)
provider/provider.go (2)
  • ConditionsByCap (472-475)
  • FileChange (466-470)
parser/rule_parser.go (2)
engine/conditions.go (2)
  • RuleSet (93-99)
  • Rule (101-107)
provider/provider.go (3)
  • InternalProviderClient (450-453)
  • ConditionsByCap (472-475)
  • Capability (82-86)
lsp/base_service_client/symbol_cache.go (5)
lsp/protocol/tsprotocol.go (5)
  • DocumentSymbol (1388-1413)
  • WorkspaceSymbol (5052-5063)
  • Range (3559-3564)
  • SelectionRange (3778-3783)
  • Pattern (3424-3424)
provider/provider.go (3)
  • InitConfig (123-159)
  • ConditionsByCap (472-475)
  • ProviderContext (349-354)
lsp/base_service_client/base_capabilities.go (1)
  • ReferencedCondition (30-35)
provider/lib.go (3)
  • FileSearcher (19-28)
  • IncludeExcludeConstraints (38-41)
  • SearchCriteria (33-36)
lsp/base_service_client/base_service_client.go (1)
  • SymbolSearchHelper (165-185)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (5)
  • GitHub Check: test
  • GitHub Check: test (windows-latest, windows, amd64)
  • GitHub Check: test (ubuntu-latest, linux, amd64)
  • GitHub Check: test (macos-latest, darwin, arm64)
  • GitHub Check: test (ubuntu-latest, linux, arm64)
🔇 Additional comments (24)
examples/nodejs/package.json (1)

1-7: Minimal, well-structured Node.js example workspace.

The dependency versions are sound and follow current best practices. React 19.2 combined with @types/react@^19.2.6 provides type safety for the latest stable version. The caret versioning strategy is appropriate for an example workspace.

demo-local.Dockerfile (1)

13-13: Appropriate Docker build step for Node.js dependencies.

Placement and structure follow the existing pattern for environment setup. The npm install will correctly resolve dependencies from the package.json added in the same PR.

demo-output.yaml (3)

603-615: Node.js provider symbol caching working correctly.

The line number adjustments for node-sample-rule-001 and node-sample-rule-002 reflect accurate symbol matching after the Prepare() phase. These are expected outcomes as the Node.js provider now caches document symbols before evaluation.

Also applies to: 626-641


642-652: New test rule validates module filtering behavior.

node-sample-rule-003 tests that module files are correctly excluded from matching. This addition expands test coverage and aligns with the PR's goal of improving symbol-based rule evaluation through the Prepare() function.


802-807: JSX support expansion confirmed.

The addition of LegacyComponent.jsx under test-tsx-support-00010 demonstrates that the Node.js provider now correctly identifies React references across both TypeScript (.tsx) and JavaScript (.jsx) files. This is a positive outcome of the symbol caching improvements.

provider/lib.go (1)

75-85: All walkDir call sites now correctly propagate exclude information

Passing excludedDirs and excludedPatterns into every walkDirFunc invocation keeps traversal and final filtering consistent (search-criteria scan, includedDirs, additionalPaths, and basePath-only scan all respect the same exclude set). No functional issues spotted here.

Also applies to: 102-113, 140-150, 160-169, 173-181

external-providers/generic-external-provider/pkg/server_configurations/yaml_language_server/service_client.go (1)

103-112: New LSP base client argument wiring looks correct

Passing nil as the new final argument to base.NewLSPServiceClientBase is consistent with the updated constructor and keeps YAML provider behavior unchanged (no custom symbol search helper).

external-providers/generic-external-provider/pkg/server_configurations/generic/service_client.go (1)

93-99: Generic service client base initialization remains consistent

The added nil argument to base.NewLSPServiceClientBase aligns with the new signature and keeps the generic provider behavior intact (no symbol search helper).

external-providers/generic-external-provider/go.mod (1)

5-13: Dependency updates look aligned with new features; verify module hygiene

Upgrading github.com/konveyor/analyzer-lsp and adding golang.org/x/sync as indirect both match the new symbol cache / preparation work. Please ensure go mod tidy has been run so there are no unintended extra changes in this module.

Also applies to: 15-39

provider/provider.go (2)

433-441: FullPrepareResponse mirrors existing aggregation patterns correctly

The new FullPrepareResponse mirrors FullNotifyFileChangesResponse and correctly aggregates errors with errors.Join, while still calling each client even if previous ones fail.


472-482: ConditionsByCap and Prepare API shape look appropriate

ConditionsByCap cleanly groups raw, serialized conditions per capability, and the ServiceClient.Prepare hook provides a clear pre‑evaluation extension point without changing existing Evaluate semantics.

external-providers/dotnet-external-provider/pkg/dotnet/service_client.go (1)

41-43: No-op Prepare correctly satisfies new ServiceClient contract

The added Prepare method safely returns nil, keeping current behavior unchanged while satisfying the extended provider.ServiceClient interface and leaving room for future warmup logic.

provider/internal/builtin/service_client.go (1)

54-56: Builtin Prepare hook is a safe no-op

Adding Prepare as a no-op keeps the builtin provider compatible with the new lifecycle without altering existing evaluation behavior.

lsp/base_service_client/symbol_cache.go (1)

274-283: compileSymbolQueryRegex behavior is sound for simple and regex queries

The compileSymbolQueryRegex helper correctly treats queries without regex metacharacters as exact, case-insensitive matches (^...$ with QuoteMeta), while allowing power users to pass full regexes when needed. This is a good balance between usability and flexibility.

external-providers/generic-external-provider/pkg/server_configurations/pylsp/service_client.go (1)

72-77: LGTM!

The addition of nil as the symbol-search helper parameter maintains existing behavior while aligning with the updated base client constructor signature.

external-providers/yq-external-provider/pkg/yq_provider/provider.go (1)

129-131: LGTM!

The no-op Prepare implementation correctly implements the new provider interface method, establishing the hook for future pre-evaluation warmup logic.

provider/internal/builtin/provider.go (1)

149-152: LGTM!

The no-op Prepare implementation with a clear TODO comment establishes the infrastructure hook while documenting the intended future use for pre-processing file content searches.

external-providers/dotnet-external-provider/pkg/dotnet/provider.go (1)

203-205: LGTM!

The no-op Prepare implementation correctly satisfies the provider interface, establishing the preparation hook for future enhancement.

external-providers/java-external-provider/pkg/java_external_provider/provider.go (1)

520-522: LGTM!

The no-op Prepare implementation correctly implements the provider interface method, maintaining consistency with other providers in the preparation phase infrastructure.

external-providers/java-external-provider/pkg/java_external_provider/service_client.go (1)

111-113: LGTM!

The no-op Prepare implementation on the service client correctly implements the interface, aligning with the provider-level preparation infrastructure.

provider/grpc/provider.go (1)

245-247: LGTM!

The Prepare implementation correctly orchestrates the preparation phase across all service clients using provider.FullPrepareResponse, maintaining consistency with the patterns used in Evaluate, GetDependencies, and other orchestration methods.

external-providers/yq-external-provider/pkg/yq_provider/service_client.go (1)

59-61: LGTM!

The no-op Prepare implementation correctly satisfies the service client interface, establishing the preparation hook while maintaining consistency with other providers.

provider/provider_test.go (1)

22-24: No-op Prepare correctly satisfies the Client interface in tests

This stub is sufficient for tests that don't care about preparation behavior and keeps the fake in sync with the production interface.

lsp/base_service_client/base_capabilities.go (1)

34-35: Embedding ProviderContext and updated GetAllDeclarations usage look consistent

Inlining provider.ProviderContext into ReferencedCondition cleanly exposes the shared provider fields to YAML without changing existing behavior, and switching to sc.GetAllDeclarations(ctx, query) matches the simplified API while still performing workspace/dependency filtering via BaseConfig. No issues from this change alone.

Also applies to: 57-57

Signed-off-by: Pranav Gaikwad <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

♻️ Duplicate comments (2)
lsp/base_service_client/symbol_cache.go (1)

128-198: Add exclusions for additional workspace folders.

The exclude patterns in GetDocumentUris are currently built only from primaryPath (lines 173-181), but additionalPaths from workspace folders also need corresponding exclusions. Without this, heavy directories like node_modules, vendor, etc., in additional workspace folders won't be excluded from the search.

Apply this approach to add exclusions for each additional path:

+	allExcludes := []string{}
+	for _, basePath := range append([]string{primaryPath}, additionalPaths...) {
+		allExcludes = append(allExcludes,
+			filepath.Join(basePath, "node_modules"),
+			filepath.Join(basePath, "vendor"),
+			filepath.Join(basePath, ".git"),
+			filepath.Join(basePath, "dist"),
+			filepath.Join(basePath, "build"),
+			filepath.Join(basePath, "target"),
+			filepath.Join(basePath, ".venv"),
+			filepath.Join(basePath, "venv"),
+		)
+	}
 	searcher := provider.FileSearcher{
 		BasePath:        primaryPath,
 		AdditionalPaths: additionalPaths,
 		ProviderConfigConstraints: provider.IncludeExcludeConstraints{
-			ExcludePathsOrPatterns: []string{
-				filepath.Join(primaryPath, "node_modules"),
-				filepath.Join(primaryPath, "vendor"),
-				filepath.Join(primaryPath, ".git"),
-				filepath.Join(primaryPath, "dist"),
-				filepath.Join(primaryPath, "build"),
-				filepath.Join(primaryPath, "target"),
-				filepath.Join(primaryPath, ".venv"),
-				filepath.Join(primaryPath, "venv"),
-			},
+			ExcludePathsOrPatterns: allExcludes,
 		},

Based on learnings

external-providers/generic-external-provider/pkg/server_configurations/nodejs/service_client.go (1)

196-212: Fix CodeLocation to use zero-based line numbers and preserve multi-line spans.

According to the Position struct documentation (provider/provider.go lines 323-341), Line should be zero-based. Currently:

  1. CodeLocation.StartPosition.Line and EndPosition.Line both use lineNumber (which is 1-based)
  2. Multi-line symbols are flattened because start and end use the same line number

Apply this diff to use zero-based positions and preserve spans:

 		lineNumber := int(baseLocation.Range.Start.Line) + 1
 		incident := provider.IncidentContext{
 			FileURI:    u,
 			LineNumber: &lineNumber,
 			Variables: map[string]interface{}{
 				"file": baseLocation.URI,
 			},
 			CodeLocation: &provider.Location{
 				StartPosition: provider.Position{
-					Line:      float64(lineNumber),
+					Line:      float64(baseLocation.Range.Start.Line),
 					Character: float64(baseLocation.Range.Start.Character),
 				},
 				EndPosition: provider.Position{
-					Line:      float64(lineNumber),
+					Line:      float64(baseLocation.Range.End.Line),
 					Character: float64(baseLocation.Range.End.Character),
 				},
 			},
 		}

Based on learnings

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between b068b33 and 6461afa.

📒 Files selected for processing (4)
  • external-providers/generic-external-provider/pkg/server_configurations/nodejs/service_client.go (5 hunks)
  • external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go (1 hunks)
  • lsp/base_service_client/symbol_cache.go (1 hunks)
  • provider/grpc/service_client.go (2 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-10-21T16:27:59.216Z
Learnt from: shawn-hurley
Repo: konveyor/analyzer-lsp PR: 860
File: provider/server.go:251-259
Timestamp: 2025-10-21T16:27:59.216Z
Learning: In provider/server.go, the Init method uses context.Background() for creating the RPC client via GetProviderRPCClient because this RPC client is stored in the InitConfig and used across multiple subsequent requests (Evaluate, GetDependencies, etc.). The RPC client's lifecycle is managed separately through explicit Stop calls, not tied to the Init request's context.

Applied to files:

  • provider/grpc/service_client.go
🧬 Code graph analysis (4)
external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go (5)
lsp/base_service_client/base_service_client.go (1)
  • SymbolSearchHelper (165-185)
provider/provider.go (2)
  • InitConfig (123-159)
  • ConditionsByCap (472-475)
provider/lib.go (3)
  • FileSearcher (19-28)
  • IncludeExcludeConstraints (38-41)
  • SearchCriteria (33-36)
lsp/protocol/tsprotocol.go (2)
  • Pattern (3424-3424)
  • WorkspaceSymbol (5052-5063)
lsp/base_service_client/symbol_cache.go (2)
  • WorkspaceSymbolDefinitionsPair (27-30)
  • NewDefaultSymbolCacheHelper (270-275)
provider/grpc/service_client.go (2)
provider/internal/grpc/library.pb.go (9)
  • NotifyFileChangesRequest (1546-1552)
  • NotifyFileChangesRequest (1565-1565)
  • NotifyFileChangesRequest (1580-1582)
  • ConditionsByCapability (1642-1648)
  • ConditionsByCapability (1661-1661)
  • ConditionsByCapability (1676-1678)
  • PrepareRequest (1694-1700)
  • PrepareRequest (1713-1713)
  • PrepareRequest (1728-1730)
provider/provider.go (1)
  • ConditionsByCap (472-475)
external-providers/generic-external-provider/pkg/server_configurations/nodejs/service_client.go (3)
provider/provider.go (5)
  • Config (88-98)
  • ProviderContext (349-354)
  • Location (319-322)
  • IncidentContext (309-317)
  • Position (324-342)
external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go (1)
  • NewNodejsSymbolCacheHelper (178-184)
lsp/base_service_client/base_service_client.go (2)
  • LSPServiceClientBase (120-158)
  • NewLspServiceClientEvaluator (77-94)
lsp/base_service_client/symbol_cache.go (5)
lsp/protocol/tsprotocol.go (5)
  • DocumentSymbol (1388-1413)
  • WorkspaceSymbol (5052-5063)
  • Range (3559-3564)
  • SelectionRange (3778-3783)
  • Pattern (3424-3424)
provider/provider.go (3)
  • InitConfig (123-159)
  • ConditionsByCap (472-475)
  • ProviderContext (349-354)
lsp/base_service_client/base_capabilities.go (1)
  • ReferencedCondition (30-35)
provider/lib.go (3)
  • FileSearcher (19-28)
  • IncludeExcludeConstraints (38-41)
  • SearchCriteria (33-36)
lsp/base_service_client/base_service_client.go (1)
  • SymbolSearchHelper (165-185)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: test
  • GitHub Check: test (windows-latest, windows, amd64)
  • GitHub Check: test (macos-latest, darwin, amd64)
  • GitHub Check: test (ubuntu-latest, linux, amd64)
  • GitHub Check: test (ubuntu-latest, linux, arm64)
  • GitHub Check: test (macos-latest, darwin, arm64)
  • GitHub Check: benchmark (windows-latest, windows)
  • GitHub Check: benchmark (ubuntu-latest, linux)
  • GitHub Check: benchmark (macos-latest, mac)
🔇 Additional comments (5)
provider/grpc/service_client.go (1)

202-226: LGTM! Prepare method implementation looks solid.

The new Prepare method correctly:

  • Converts provider.ConditionsByCap to protobuf format
  • Includes the provider ID in the request
  • Handles both RPC errors and application-level errors from the response

The implementation follows the same pattern as other methods in this file (e.g., Evaluate, GetDependencies).

lsp/base_service_client/symbol_cache.go (1)

74-81: LGTM! InvalidateAll now correctly clears both caches.

The fix addresses the previous review concern by clearing both workspace symbols and document symbols under their respective mutexes.

external-providers/generic-external-provider/pkg/server_configurations/nodejs/service_client.go (2)

52-56: Good defensive programming.

Initializing ProviderSpecificConfig when nil prevents potential panics when setting workspaceFolders.


173-190: LGTM! Workspace folder and dependency filtering are now correct.

The previous review concerns have been addressed:

  • Line 173 now guards against empty WorkspaceFolders to prevent panics
  • Lines 177-190 correctly use a skip flag to filter out symbols from dependency folders
external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go (1)

106-157: LGTM! Scoped query matching now uses platform-independent URI format.

Lines 127 and 137 correctly use fmt.Sprintf("%s/%s", *query.Package, *query.Scope) instead of filepath.Join, which ensures URI matching works correctly across all platforms (avoiding Windows backslash issues).

Signed-off-by: Pranav Gaikwad <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (1)
external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go (1)

187-224: Consider defensive checks for empty package or scope components.

The parseQuery function can assign empty strings to Package or Scope in certain edge cases:

  • Pattern "package/" yields Package="package", Scope=""
  • Pattern "package/#query" yields Package="package", Scope=""

While these patterns may not occur in practice, adding checks before assigning could prevent subtle bugs:

 	pkg := prefix[:slashIdx]
 	scope := prefix[slashIdx+1:]
-	if pkg != "" {
+	if strings.TrimSpace(pkg) != "" {
 		result.Package = &pkg
 	}
-	if scope != "" {
+	if strings.TrimSpace(scope) != "" {
 		result.Scope = &scope
 	}

This ensures only non-empty, non-whitespace values are assigned.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6461afa and 6949612.

📒 Files selected for processing (1)
  • external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go (1 hunks)
🧰 Additional context used
🧠 Learnings (1)
📚 Learning: 2025-11-21T14:12:52.206Z
Learnt from: pranavgaikwad
Repo: konveyor/analyzer-lsp PR: 975
File: external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go:29-78
Timestamp: 2025-11-21T14:12:52.206Z
Learning: In the analyzer-lsp codebase, additional workspace folders (additionalPaths) used in FileSearcher are already vetted and scoped, so they do not require additional exclusion patterns for directories like node_modules, .git, dist, etc. Only the primary path needs these exclusions.

Applied to files:

  • external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go
🧬 Code graph analysis (1)
external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go (5)
lsp/base_service_client/base_service_client.go (1)
  • SymbolSearchHelper (165-185)
provider/provider.go (2)
  • InitConfig (123-159)
  • ConditionsByCap (472-475)
provider/lib.go (3)
  • FileSearcher (19-28)
  • IncludeExcludeConstraints (38-41)
  • SearchCriteria (33-36)
lsp/protocol/tsprotocol.go (2)
  • Pattern (3424-3424)
  • WorkspaceSymbol (5052-5063)
lsp/base_service_client/symbol_cache.go (2)
  • WorkspaceSymbolDefinitionsPair (27-30)
  • NewDefaultSymbolCacheHelper (270-275)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: test
  • GitHub Check: test (macos-latest, darwin, amd64)
  • GitHub Check: test (windows-latest, windows, amd64)
  • GitHub Check: test (ubuntu-latest, linux, arm64)
  • GitHub Check: test (macos-latest, darwin, arm64)
  • GitHub Check: test (ubuntu-latest, linux, amd64)
  • GitHub Check: benchmark (ubuntu-latest, linux)
  • GitHub Check: benchmark (windows-latest, windows)
  • GitHub Check: benchmark (macos-latest, mac)
🔇 Additional comments (1)
external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go (1)

75-99: No issues found with referencedCondition type usage.

The referencedCondition type is properly defined in the same package at external-providers/generic-external-provider/pkg/server_configurations/nodejs/service_client.go (lines 122-127) and correctly matches the unmarshal pattern used in MatchFileContentByConditions. The code in lines 75-99 properly accesses the Referenced.Pattern field as expected from the type definition.

Signed-off-by: Pranav Gaikwad <[email protected]>
Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 0

♻️ Duplicate comments (1)
external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go (1)

106-157: This concern was previously flagged.

Previous reviews identified that lines 127 and 137 should prepend @ when matching npm scoped packages (e.g., @types/node). The current implementation requires users to explicitly include the @ prefix in their patterns, which was noted as unintuitive. Consider the previous suggestion to use fmt.Sprintf("@%s/%s", *query.Package, *query.Scope).

🧹 Nitpick comments (1)
lsp/base_service_client/base_service_client.go (1)

142-151: Consider buffering strategy for symbolCacheUpdateChan.

The channel is buffered to 10 items (line 264), which may cause blocking if many file updates arrive simultaneously in large codebases. Consider whether this capacity is sufficient or if a larger buffer or different queuing strategy might be needed for responsive symbol cache updates.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 6949612 and ab0fba5.

📒 Files selected for processing (2)
  • external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go (1 hunks)
  • lsp/base_service_client/base_service_client.go (5 hunks)
🧰 Additional context used
🧠 Learnings (2)
📚 Learning: 2025-11-21T14:12:52.206Z
Learnt from: pranavgaikwad
Repo: konveyor/analyzer-lsp PR: 975
File: external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go:29-78
Timestamp: 2025-11-21T14:12:52.206Z
Learning: In the analyzer-lsp codebase, additional workspace folders (additionalPaths) used in FileSearcher are already vetted and scoped, so they do not require additional exclusion patterns for directories like node_modules, .git, dist, etc. Only the primary path needs these exclusions.

Applied to files:

  • external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go
📚 Learning: 2025-10-21T16:25:40.903Z
Learnt from: shawn-hurley
Repo: konveyor/analyzer-lsp PR: 860
File: lsp/base_service_client/base_service_client.go:212-214
Timestamp: 2025-10-21T16:25:40.903Z
Learning: In `lsp/base_service_client/base_service_client.go`, when an RPC connection is provided via `c.RPC` (not nil), it is treated as already initialized and no handler setup is performed. This is known technical debt that needs to be addressed in the future to ensure LSP notifications like `textDocument/publishDiagnostics` are properly processed.

Applied to files:

  • lsp/base_service_client/base_service_client.go
🧬 Code graph analysis (2)
external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go (5)
lsp/base_service_client/base_service_client.go (1)
  • SymbolSearchHelper (165-185)
provider/provider.go (2)
  • InitConfig (123-159)
  • ConditionsByCap (472-475)
provider/lib.go (3)
  • FileSearcher (19-28)
  • IncludeExcludeConstraints (38-41)
  • SearchCriteria (33-36)
lsp/protocol/tsprotocol.go (2)
  • Pattern (3424-3424)
  • WorkspaceSymbol (5052-5063)
lsp/base_service_client/symbol_cache.go (2)
  • WorkspaceSymbolDefinitionsPair (27-30)
  • NewDefaultSymbolCacheHelper (270-275)
lsp/base_service_client/base_service_client.go (3)
lsp/base_service_client/symbol_cache.go (4)
  • SymbolCache (20-25)
  • WorkspaceSymbolDefinitionsPair (27-30)
  • NewDocumentSymbolCache (32-37)
  • NewDefaultSymbolCacheHelper (270-275)
lsp/protocol/tsprotocol.go (15)
  • URI (4673-4673)
  • Log (5479-5479)
  • WorkspaceSymbol (5052-5063)
  • Location (2389-2392)
  • Range (3559-3564)
  • Info (5477-5477)
  • OrPLocation_workspace_symbol (2764-2766)
  • DocumentURI (1460-1460)
  • BaseSymbolInformation (51-65)
  • Text (5341-5341)
  • TextDocumentIdentifier (4478-4481)
  • DocumentSymbol (1388-1413)
  • DocumentSymbolParams (1448-1453)
  • File (5582-5582)
  • Position (3452-3466)
provider/provider.go (5)
  • ConditionsByCap (472-475)
  • InitConfig (123-159)
  • FileChange (466-470)
  • Location (319-322)
  • Position (324-342)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (9)
  • GitHub Check: test
  • GitHub Check: test (windows-latest, windows, amd64)
  • GitHub Check: test (ubuntu-latest, linux, arm64)
  • GitHub Check: test (macos-latest, darwin, arm64)
  • GitHub Check: test (macos-latest, darwin, amd64)
  • GitHub Check: test (ubuntu-latest, linux, amd64)
  • GitHub Check: benchmark (ubuntu-latest, linux)
  • GitHub Check: benchmark (windows-latest, windows)
  • GitHub Check: benchmark (macos-latest, mac)
🔇 Additional comments (18)
external-providers/generic-external-provider/pkg/server_configurations/nodejs/symbol_cache_helper.go (7)

1-24: LGTM!

The package structure, imports, and type definition are well-organized. Embedding base.SymbolSearchHelper allows extending the default behavior while reusing base functionality.


26-73: LGTM!

The document URI discovery logic correctly normalizes paths, uses appropriate file filters for TypeScript/JavaScript files, and excludes common non-source directories.

Based on learnings


75-104: LGTM!

The content matching logic appropriately distinguishes between literal and regex patterns, uses whole-word matching for literals, and handles errors gracefully by logging and continuing.


159-176: LGTM!

The condition matching logic correctly delegates to MatchSymbolByPatterns after unmarshaling the YAML condition.


178-184: LGTM!

The constructor correctly initializes the Node.js symbol search helper by embedding the default cache helper.


186-229: LGTM!

The query parsing logic correctly handles all pattern variations: plain queries without #, patterns with package/scope prefixes, and gracefully handles edge cases.


231-285: LGTM!

The helper functions correctly implement whole-word matching with proper token boundary detection. The identifier character set appropriately includes JavaScript/TypeScript-specific characters like $.

lsp/base_service_client/base_service_client.go (11)

160-185: LGTM!

The SymbolSearchHelper interface provides a clean abstraction for language-specific symbol search behavior. The documentation clearly distinguishes between Prepare-phase and Evaluate-phase operations.


187-296: LGTM!

The constructor correctly initializes the symbol cache infrastructure, provides sensible defaults, and properly starts the background update handler before the client is ready for use.


315-333: LGTM!

The file change notification correctly invalidates affected cache entries and queues updates with proper synchronization. The wait group is incremented before sending to the channel, ensuring safe coordination.


336-348: LGTM!

The Prepare method correctly initiates symbol cache population by calling the helper's GetDocumentUris and queuing discovered files for processing. Wait group usage properly tracks all async operations.


423-456: LGTM!

The refactored GetAllDeclarations appropriately prefers the LSP's native workspace/symbol request and falls back to cached symbol search when needed. The wait for pending updates ensures cache consistency.


479-573: LGTM!

The symbol cache population orchestrates multiple LSP operations correctly: text search, definition lookup, document symbol queries, and symbol matching. Error handling appropriately logs and continues, allowing batch processing to complete despite individual file errors.


575-620: LGTM!

The symbol cache update handler correctly implements the worker pattern with graceful shutdown. The drain logic prevents wait group deadlock by decrementing without processing when shutting down.


622-694: LGTM!

The document symbol query helpers correctly manage the LSP lifecycle (didOpen/didClose) around symbol requests and cache results to avoid redundant calls. The toURI utility properly handles both URI-formatted and plain file paths.


696-759: LGTM!

The content search efficiently scans line-by-line, queries document symbols only once, and handles cases where symbol information is unavailable by creating a fallback workspace symbol with the matched text.


761-786: LGTM!

The definition lookup correctly wraps the LSP call with didOpen/didClose and handles errors gracefully. Note that line 768 uses location.Range.End as the position, which may be intentional to resolve definitions at the end of matched text.


788-859: LGTM!

The symbol finding utilities correctly implement tree traversal with best-match selection based on smallest overlapping range. The range comparison and length calculation logic is sound. Note that line 796 calls preferredRange, which should be defined elsewhere in the codebase.

@pranavgaikwad pranavgaikwad merged commit b3f742c into konveyor:main Nov 21, 2025
22 of 24 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants