Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: Enhance dbt-power-user Side Panel to Show Model Info for Non-SQL Files #1475

Open
wants to merge 5 commits into
base: master
Choose a base branch
from

Conversation

toukoudo
Copy link

@toukoudo toukoudo commented Oct 21, 2024

Overview

Problem

  • Our team creates dbt docs for each model, named after the model names.
    • e.g. We have a dbt docs file a_model.md for a dbt model a_model.
  • The Side Panel of dbt-power-user only shows the model information when a SQL file is active.
  • We want to see the model information even when a dbt docs file is active.

Solution

  • In modelTreeviewProvider.ts, path.basename(fileName, ".sql") is used to get a basename (=model name).
    • If an active file is not a SQL file, the extension is not removed, and the model cannot be fetched.
  • I changed the code to use path.parse(fileName).name to get a basename. In this way, we can get a basename of any extension.

Screenshot/Demo

Single model in a YAML file:
スクリーンショット 2024-11-10 18 19 00

Multiple mode in a YAML file. The model at the cursor is shown in a side panel:
スクリーンショット 2024-11-10 18 18 49

How to test

  1. Open dbt-power-user extension in a development host.
  2. Open a dbt project folder.
  3. Open a dbt docs file, and the Side Panel of dbt-power-user should show the model information.

Checklist

  • I have run this code and it appears to resolve the stated issue.
  • README.md updated and added information about my change.

Important

Enhance dbt-power-user side panel to display model info for non-SQL files by updating file name parsing in modelTreeviewProvider.ts.

  • Behavior:
    • Update modelTreeviewProvider.ts to use path.parse(fileName).name instead of path.basename(fileName, ".sql").
    • Allows side panel to show model information for non-SQL files.
  • Files:
    • Changes in getChildren() method of ModelTreeviewProvider and DocumentationTreeviewProvider classes.

This description was created by Ellipsis for 1d359c0. It will automatically update as commits are pushed.

Summary by CodeRabbit

  • New Features

    • Enhanced model retrieval logic in the tree view, improving accuracy based on the active editor's content.
    • Introduced a new function to extract model names from the active YAML document.
    • Added responsiveness to tree data refresh when text editor selection changes.
  • Bug Fixes

    • Improved error handling in the new model name retrieval function to ensure graceful failures.
  • Documentation

    • Updated documentation to reflect changes in method signatures and new functionalities.

Copy link
Contributor

coderabbitai bot commented Oct 21, 2024

Walkthrough

The pull request introduces modifications to the getChildren methods in the ModelTreeviewProvider and DocumentationTreeviewProvider classes. It replaces the previous reliance on path.basename for file name extraction with a new utility function, getModelNameInActiveEditor, which retrieves model names based on the active editor's content. Additionally, both providers now include an event listener for window.onDidChangeTextEditorSelection to refresh tree data when the selection changes, enhancing the accuracy and responsiveness of model retrieval.

Changes

File Change Summary
src/treeview_provider/modelTreeviewProvider.ts Modified getChildren methods in ModelTreeviewProvider and DocumentationTreeviewProvider to use getModelNameInActiveEditor for model name retrieval and added an event listener for text editor selection changes.
src/utils.ts Added getModelNameInActiveEditor function to retrieve model names from the active YAML document and updated import statements.

Possibly related PRs

  • Support model versions #1436: This PR modifies the model retrieval logic, which is closely related to the changes in the getChildren methods of the ModelTreeviewProvider and DocumentationTreeviewProvider in the main PR, as both involve enhancements to how models are accessed and processed.
  • fix: preserve comments and formatting in yml #1468: This PR introduces improvements to YAML document handling, which aligns with the main PR's addition of the getModelNameInActiveEditor function that interacts with YAML documents, enhancing model name retrieval based on the active editor's content.
  • chore: add isModelDoc Attribute for AI learning #1497: This PR adds an isModelDoc attribute to improve AI learning related to model documentation, which connects to the main PR's focus on enhancing model retrieval and context awareness in the editor.

Suggested reviewers

  • mdesmet

Tip

CodeRabbit's docstrings feature is now available as part of our Early Access Program! Simply use the command @coderabbitai generate docstrings to have CodeRabbit automatically generate docstrings for your pull request.


Thank you for using CodeRabbit. We offer it for free to the OSS community and would appreciate your support in helping us grow. If you find it useful, would you consider giving us a shout-out on your favorite social media?

❤️ Share
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Generate unit testing code for this file.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai generate unit testing code for this file.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and generate unit testing code.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR. (Beta)
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@ellipsis-dev ellipsis-dev bot left a comment

Choose a reason for hiding this comment

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

👍 Looks good to me! Reviewed everything up to 1d359c0 in 17 seconds

More details
  • Looked at 30 lines of code in 1 files
  • Skipped 0 files when reviewing.
  • Skipped posting 2 drafted comments based on config settings.
1. src/treeview_provider/modelTreeviewProvider.ts:108
  • Draft comment:
    Simplify the code by directly using path.parse without the intermediate variable.
    const fileName = path.parse(window.activeTextEditor!.document.fileName).name;
  • Reason this comment was not posted:
    Confidence changes required: 10%
    The change from path.basename to path.parse is correct and aligns with the PR description. However, the code can be simplified by directly using path.parse without the intermediate variable.
2. src/treeview_provider/modelTreeviewProvider.ts:221
  • Draft comment:
    Please specify a return type for the getChildren function to improve code readability and maintainability. This applies to both ModelTreeviewProvider and DocumentationTreeviewProvider classes.
  • Reason this comment was not posted:
    Comment was on unchanged code.

Workflow ID: wflow_4hKAYrB8x5iEcsgz


You can customize Ellipsis with 👍 / 👎 feedback, review rules, user-specific overrides, quiet mode, and more.

Copy link
Contributor

@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

🧹 Outside diff range and nitpick comments (1)
src/treeview_provider/modelTreeviewProvider.ts (1)

224-226: LGTM! Consistent improvement in file name parsing.

The change from path.basename to path.parse in the DocumentationTreeviewProvider class is consistent with the earlier modification in the ModelTreeviewProvider class. This ensures that the documentation tree view can also handle non-SQL files effectively.

For improved code consistency, consider extracting the file name parsing logic into a shared utility function:

function getFileNameWithoutExtension(filePath: string): string {
  return path.parse(filePath).name;
}

This function can then be used in both ModelTreeviewProvider and DocumentationTreeviewProvider classes, reducing code duplication and improving maintainability.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Files that changed from the base of the PR and between f596081 and 1d359c0.

📒 Files selected for processing (1)
  • src/treeview_provider/modelTreeviewProvider.ts (2 hunks)
🧰 Additional context used
🔇 Additional comments (2)
src/treeview_provider/modelTreeviewProvider.ts (2)

107-109: LGTM! Improved file name parsing for broader file type support.

The change from path.basename to path.parse enhances the extension's capability to handle non-SQL files, aligning with the PR's objective. This modification allows the Side Panel to display model information for various file types, including dbt documentation files.


Line range hint 1-487: Overall assessment: Changes effectively implement the PR objectives.

The modifications in both ModelTreeviewProvider and DocumentationTreeviewProvider classes successfully enhance the dbt-power-user extension to support non-SQL files, particularly dbt documentation files. The changes are minimal, focused, and consistent, which reduces the risk of introducing new issues while effectively implementing the desired functionality.

To further improve the PR:

  1. Consider adding unit tests to verify the behavior with different file types.
  2. Update the README.md to reflect this new capability, as mentioned in the PR checklist.
  3. Implement the suggested shared utility function for file name parsing to improve code consistency and maintainability.

Great job on enhancing the extension's functionality!

To ensure the changes work as expected across different file types, please run the following verification script:

@anandgupta42
Copy link
Contributor

@toukoudo Thanks for your contribution. How will this work if there are multiple models defined within the same markdown file?

@toukoudo
Copy link
Author

toukoudo commented Oct 22, 2024

Thanks for the comment! @anandgupta42

How will this work if there are multiple models defined within the same markdown file?

Sadly, this PR doesn't handle multiple models in the same markdown file.
I'll be happy if the Side Panel works in the situation, but for now, it's set up to work with one model per file.

(If necessary, I would like to create another pull request to address that functionality in the future! )

@mdesmet
Copy link
Contributor

mdesmet commented Oct 23, 2024

We should always aim at bringing something to production that works reliably. So we need to know what's the behaviour in case of multiple models. We could use the texteditor position in case of multiple models.

@mdesmet
Copy link
Contributor

mdesmet commented Oct 23, 2024

Also when you say markdown, you mean yaml file right? Again it is better to support only yaml files if that is the ones that you tested. In case of markdown I'm not totally sure if this currently is associated with a model based on file basename. So let's only support yaml files for now.

Also the yaml file name is completely free and not linked to a model. You will need to get the model name from the yaml instead.

Comment on lines 107 to 124
const fileName = path.parse(
window.activeTextEditor!.document.fileName,
".sql",
);
).name;
Copy link
Contributor

Choose a reason for hiding this comment

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

We should adapt this code to follow this logic for sql files, but different logic for yaml files. to use the texteditor position in case of multiple models within the yaml file, or select the single model in case of a single model in the yaml file

@toukoudo
Copy link
Author

Thank you for the feedback!

I'll work on implementing the code to extract the model name from the YAML file itself, rather than relying on the file name.

@toukoudo toukoudo force-pushed the open-panel-other-than-sql branch from 4a99930 to 1e8243e Compare November 10, 2024 09:33
Copy link
Contributor

@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: 4

🧹 Outside diff range and nitpick comments (2)
src/utils.ts (1)

379-383: Add documentation for YAML structure assumptions.

The code makes assumptions about the YAML structure that should be documented for maintainability.

Add a comment block explaining the expected YAML structure:

+  // Expected YAML structure:
+  // ```yaml
+  // models:
+  //   - name: model_name
+  //     # ... other model properties
+  // ```
   const models = (parsedYaml.contents as any).items.find(
     (y: any) => y.key.value === "models",
   ).value.items;
src/treeview_provider/modelTreeviewProvider.ts (1)

112-130: Consider implementing position-based model selection

As discussed in the PR comments, the current implementation doesn't handle multiple models within the same YAML file. While the onDidChangeTextEditorSelection event listener is in place, the actual position-based model selection logic is missing.

Consider these improvements for a future PR:

  1. Enhance getModelNameInActiveEditor to accept cursor position as a parameter
  2. Use the cursor position to determine which model definition the cursor is currently in
  3. Return the model name based on the YAML structure at that position
  4. Add tests to verify the behavior with multiple models in a single file

Would you like me to create a GitHub issue to track this enhancement?

Also applies to: 245-259

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1d359c0 and 1e8243e.

📒 Files selected for processing (2)
  • src/treeview_provider/modelTreeviewProvider.ts (6 hunks)
  • src/utils.ts (2 hunks)
🔇 Additional comments (2)
src/utils.ts (1)

11-11: LGTM: Import additions are appropriate.

The new imports are necessary for the enhanced functionality:

  • window enables active editor interaction
  • parseDocument provides structured YAML parsing capabilities

Also applies to: 14-14

src/treeview_provider/modelTreeviewProvider.ts (1)

58-60: LGTM: Event listener implementation for selection changes

The addition of onDidChangeTextEditorSelection event listeners in both providers enables dynamic updates of the tree view when the cursor position changes, which is essential for handling multiple models in a file.

Let's verify the refresh behavior:

Also applies to: 200-202

✅ Verification successful

Let me gather more context about the event handling implementation to ensure consistency.


Let me check one more aspect to understand the complete refresh mechanism and its triggers.


Based on the gathered evidence, I can now provide the final response:

Tree refresh implementation is consistent and well-integrated

The onDidChangeTextEditorSelection event listener's implementation is correctly integrated with the existing refresh mechanisms:

  • It properly fires the tree data change event using _onDidChangeTreeData.fire()
  • The refresh behavior aligns with other refresh triggers in the codebase
  • The implementation is consistent across both providers and works alongside the manifest change events
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check if there are any other places in the codebase where tree refresh is handled
# to ensure consistent behavior across the extension.

# Search for tree refresh patterns
rg -A 2 '_onDidChangeTreeData.fire'

Length of output: 1512


Script:

#!/bin/bash
# Check the full implementation of ModelTreeviewProvider to understand all refresh triggers
ast-grep --pattern 'class ModelTreeviewProvider {
  $$$
  constructor($$$) {
    $$$
  }
  $$$
}'

# Also check for any window.onDidChangeTextEditorSelection usage elsewhere
rg -A 2 'onDidChangeTextEditorSelection'

Length of output: 633


Script:

#!/bin/bash
# Check the constructor implementation to understand all event subscriptions
ast-grep --pattern 'constructor($$$) {
  $$$
  this._disposables = [
    $$$
  ];
  $$$
}'

# Also check for any other tree refresh mechanisms in the file
rg -B 2 -A 2 'refresh|getChildren|getTreeItem' src/treeview_provider/modelTreeviewProvider.ts

Length of output: 1756

src/utils.ts Outdated
Comment on lines 364 to 399
export function getModelNameInActiveEditor(): string {
if (
window.activeTextEditor === undefined ||
window.activeTextEditor.document.languageId !== "yaml"
) {
return "";
}

const parsedYaml = parseDocument(window.activeTextEditor.document.getText());
if (parsedYaml.contents === null) {
return "";
}
const cursorPosition = window.activeTextEditor.selection.active;
const offset = window.activeTextEditor.document.offsetAt(cursorPosition);

// parseDocument returns Pair of Key-Value
// So, we need to find the node with key "models", and extract items of it
const models = (parsedYaml.contents as any).items.find(
(y: any) => y.key.value === "models",
).value.items;

// Find a model at the current position
for (const model of models) {
// each element of models is a Pair of Key-Value, and Value is Scalar Type.
// So, we need to find the node with key "name", and extract value of it by toString
const name = model.items
.find((x: any) => x.key.value === "name")
.value.toString();

if (model.range[0] < offset && offset < model.range[1]) {
return name;
}
}

return "";
}
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Add error handling and type safety improvements.

The current implementation has several potential points of failure that should be handled more gracefully:

  1. YAML parsing errors
  2. Missing "models" key
  3. Missing "name" field in model
  4. Unsafe type assertions

Consider applying these improvements:

 export function getModelNameInActiveEditor(): string {
   if (
     window.activeTextEditor === undefined ||
     window.activeTextEditor.document.languageId !== "yaml"
   ) {
     return "";
   }

-  const parsedYaml = parseDocument(window.activeTextEditor.document.getText());
-  if (parsedYaml.contents === null) {
-    return "";
-  }
+  try {
+    const parsedYaml = parseDocument(window.activeTextEditor.document.getText());
+    if (parsedYaml.contents === null) {
+      return "";
+    }
+    const cursorPosition = window.activeTextEditor.selection.active;
+    const offset = window.activeTextEditor.document.offsetAt(cursorPosition);
+
+    const contents = parsedYaml.contents as { items?: Array<any> };
+    if (!contents.items) {
+      return "";
+    }
+
+    const modelsNode = contents.items.find(
+      (item) => item?.key?.value === "models"
+    );
+    if (!modelsNode?.value?.items) {
+      return "";
+    }
+
+    // Find a model at the current position
+    for (const model of modelsNode.value.items) {
+      if (!model?.items) continue;
+
+      const nameNode = model.items.find(
+        (item) => item?.key?.value === "name"
+      );
+      if (!nameNode?.value) continue;
+
+      if (model.range?.[0] < offset && offset < model.range?.[1]) {
+        return nameNode.value.toString();
+      }
+    }
+  } catch (error) {
+    console.error("Error parsing YAML document:", error);
+  }
+  return "";
 }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
export function getModelNameInActiveEditor(): string {
if (
window.activeTextEditor === undefined ||
window.activeTextEditor.document.languageId !== "yaml"
) {
return "";
}
const parsedYaml = parseDocument(window.activeTextEditor.document.getText());
if (parsedYaml.contents === null) {
return "";
}
const cursorPosition = window.activeTextEditor.selection.active;
const offset = window.activeTextEditor.document.offsetAt(cursorPosition);
// parseDocument returns Pair of Key-Value
// So, we need to find the node with key "models", and extract items of it
const models = (parsedYaml.contents as any).items.find(
(y: any) => y.key.value === "models",
).value.items;
// Find a model at the current position
for (const model of models) {
// each element of models is a Pair of Key-Value, and Value is Scalar Type.
// So, we need to find the node with key "name", and extract value of it by toString
const name = model.items
.find((x: any) => x.key.value === "name")
.value.toString();
if (model.range[0] < offset && offset < model.range[1]) {
return name;
}
}
return "";
}
export function getModelNameInActiveEditor(): string {
if (
window.activeTextEditor === undefined ||
window.activeTextEditor.document.languageId !== "yaml"
) {
return "";
}
try {
const parsedYaml = parseDocument(window.activeTextEditor.document.getText());
if (parsedYaml.contents === null) {
return "";
}
const cursorPosition = window.activeTextEditor.selection.active;
const offset = window.activeTextEditor.document.offsetAt(cursorPosition);
const contents = parsedYaml.contents as { items?: Array<any> };
if (!contents.items) {
return "";
}
const modelsNode = contents.items.find(
(item) => item?.key?.value === "models"
);
if (!modelsNode?.value?.items) {
return "";
}
// Find a model at the current position
for (const model of modelsNode.value.items) {
if (!model?.items) continue;
const nameNode = model.items.find(
(item) => item?.key?.value === "name"
);
if (!nameNode?.value) continue;
if (model.range?.[0] < offset && offset < model.range?.[1]) {
return nameNode.value.toString();
}
}
} catch (error) {
console.error("Error parsing YAML document:", error);
}
return "";
}

💡 Codebase verification

Based on the search results, I can now provide a final response:

The YAML structure assumption in getModelNameInActiveEditor() needs revision

The function assumes all YAML files will have a specific structure with models key containing items with name fields. However, the codebase shows:

  • The function is used specifically for dbt model YAML files (indicated by the languageId check)
  • The only YAML file found (sweep.yaml) has a completely different structure
  • The code could crash when processing non-dbt YAML files due to unsafe assumptions:
    • Unchecked access to items property
    • Direct array access without null checks
    • Assumption that models key exists and has specific structure
🔗 Analysis chain

Verify YAML structure assumptions across the codebase.

Let's verify that all dbt YAML files in the project follow the expected structure.


Let me gather more information about the YAML files in the codebase to better understand their structure and usage.


Let me try one more search to specifically understand how the YAML parsing is used in the codebase.

🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for YAML files that might not follow the expected structure
# Expected structure: Should have 'models' key with array of objects containing 'name' field

# Find all YAML files
echo "Checking YAML files structure..."
fd -e yml -e yaml --exec sh -c '
  echo "Checking: {}"
  if ! yq eval ".models" {} >/dev/null 2>&1; then
    echo "Warning: {} - missing or invalid models key"
  elif ! yq eval ".models[].name" {} >/dev/null 2>&1; then
    echo "Warning: {} - models missing name field"
  fi
'

Length of output: 430


Script:

#!/bin/bash
# Let's examine the content of YAML files to understand their structure
echo "=== Searching for YAML files ==="
fd -e yml -e yaml -x cat {}

echo -e "\n=== Searching for YAML file usage in code ==="
rg -g '*.{ts,js}' 'parseDocument.*\.ya?ml' -A 2 -B 2

Length of output: 1734


Script:

#!/bin/bash
# Search for parseDocument usage and YAML handling
echo "=== Searching for parseDocument usage ==="
rg "parseDocument" -A 5 -B 5

echo -e "\n=== Searching for YAML-related code ==="
rg "languageId.*yaml" -A 5 -B 5

Length of output: 3542

Comment on lines 245 to 259
const fileName = path.parse(
window.activeTextEditor!.document.fileName,
".sql",
).name;
const currentNodeByFileName =
event.nodeMetaMap.lookupByBaseName(fileName);
const currentNodeByFileContent = event.nodeMetaMap.lookupByBaseName(
getModelNameInActiveEditor(),
);
const currentNode = nodeMetaMap.lookupByBaseName(modelName);

const currentNode = currentNodeByFileContent || currentNodeByFileName;
if (currentNode === undefined) {
return Promise.resolve([]);
}
const modelName = currentNode.name;

Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue

Align implementation with ModelTreeviewProvider and add validation

There are two concerns with the current implementation:

  1. The model lookup logic differs from ModelTreeviewProvider. While ModelTreeviewProvider tries content-based lookup first and falls back to filename-based lookup, this implementation uses OR operator which might lead to unexpected behavior.

  2. The model name is used without validation, which could cause issues if both lookups fail.

Consider this improvement:

-     const currentNodeByFileName =
-       event.nodeMetaMap.lookupByBaseName(fileName);
-     const currentNodeByFileContent = event.nodeMetaMap.lookupByBaseName(
-       getModelNameInActiveEditor(),
-     );
-
-     const currentNode = currentNodeByFileContent || currentNodeByFileName;
+     // Try content-based lookup first
+     const currentNodeByFileContent = event.nodeMetaMap.lookupByBaseName(
+       getModelNameInActiveEditor().catch(error => {
+         console.warn('Failed to parse editor content:', error);
+         return undefined;
+       })
+     );
+     
+     // Fall back to filename-based lookup
+     const currentNode = currentNodeByFileContent ?? 
+       event.nodeMetaMap.lookupByBaseName(fileName);

      if (currentNode === undefined) {
        return Promise.resolve([]);
      }
-     const modelName = currentNode.name;
+     const modelName = currentNode?.name;

Committable suggestion skipped: line range outside the PR's diff.

Copy link
Contributor

@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

🧹 Outside diff range and nitpick comments (1)
src/utils.ts (1)

364-415: Improve type safety with interfaces.

The implementation looks good but could benefit from stronger typing. Consider defining interfaces for the YAML structure:

interface DbtModelNode {
  key?: { value: string };
  value?: { items?: DbtModelItem[] };
  range?: [number, number];
}

interface DbtModelItem {
  items?: Array<{
    key?: { value: string };
    value?: unknown;
  }>;
  range?: [number, number];
}

interface DbtYamlContents {
  items?: DbtModelNode[];
}

Then update the type assertions:

-    const contents = parsedYaml.contents as { items?: Array<any> };
+    const contents = parsedYaml.contents as DbtYamlContents;

-      (item: any) => item?.key?.value === "name",
+      (item: DbtModelItem["items"][number]) => item?.key?.value === "name",
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

📥 Commits

Reviewing files that changed from the base of the PR and between 1e8243e and adc62fc.

📒 Files selected for processing (1)
  • src/utils.ts (2 hunks)
🔇 Additional comments (2)
src/utils.ts (2)

11-11: LGTM: Import statements are appropriate.

The added imports are necessary for the new functionality:

  • window from 'vscode' for accessing the active editor
  • parseDocument from 'yaml' for parsing YAML with position information

Also applies to: 14-14


394-410: Clarify behavior for multiple models.

The current implementation returns the first model that contains the cursor position. Based on the PR discussion:

  1. How should the function behave when multiple models are defined in the same file?
  2. Should we consider adding a warning when multiple models are detected?
  3. Would it be helpful to return all applicable model names to let the caller decide?

Let's check if multiple models in a single file is a common pattern:

@toukoudo
Copy link
Author

I have updated the code to support multiple models in dbt docs.
(Demo screenshots are on the updated PR description.)

Please review again 🙇 @anandgupta42 @mdesmet

@toukoudo toukoudo requested a review from mdesmet November 13, 2024 08:15
@toukoudo
Copy link
Author

just a friendly reminder > @anandgupta42 @mdesmet

Let me know if something should be changed!

Copy link
Contributor

@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

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between adc62fc and 148516a.

📒 Files selected for processing (1)
  • src/treeview_provider/modelTreeviewProvider.ts (6 hunks)
🔇 Additional comments (4)
src/treeview_provider/modelTreeviewProvider.ts (4)

33-33: LGTM: Event handling implementation is correct

The addition of onDidChangeTextEditorSelection event listeners in both providers ensures that the tree view stays synchronized with the editor's current selection. This is essential for supporting the display of model information as users navigate through the file.

Also applies to: 58-60, 200-202


112-119: ⚠️ Potential issue

Add error handling for content-based model lookup

The content-based lookup using getModelNameInActiveEditor() needs error handling to prevent potential runtime errors if the file content cannot be parsed.

Apply this diff:

    const model_by_file_content = event.nodeMetaMap.lookupByBaseName(
-     getModelNameInActiveEditor(),
+     getModelNameInActiveEditor().catch(error => {
+       console.warn('Failed to parse editor content:', error);
+       return undefined;
+     })
    );

Likely invalid or redundant comment.


245-261: ⚠️ Potential issue

Align error handling with ModelTreeviewProvider

While this implementation correctly uses the nullish coalescing operator for fallback, it still needs error handling for getModelNameInActiveEditor(). Additionally, consider using the shared utility function proposed above for consistency.

Apply this diff:

-     const fileName = path.parse(
-       window.activeTextEditor!.document.fileName,
-     ).name;
-     // Try content-based lookup first
-     const currentNodeByFileContent = event.nodeMetaMap.lookupByBaseName(
-       getModelNameInActiveEditor(),
-     );
-     // Fall back to filename-based lookup
-     const currentNode =
-       currentNodeByFileContent ??
-       event.nodeMetaMap.lookupByBaseName(fileName);
+     const currentNode = await lookupModelByEditorContent(
+       event.nodeMetaMap,
+       window.activeTextEditor!.document.fileName
+     );

Likely invalid or redundant comment.


112-130: Verify handling of YAML files with multiple models

Let's verify that the content-based model lookup correctly handles YAML files containing multiple model definitions.

Also applies to: 245-261

✅ Verification successful

Let me try a different approach to verify the YAML handling.


Let me try one final verification to understand how models are extracted from YAML content.


Based on the gathered information, I can now provide the final response:

YAML model handling is properly implemented

The implementation correctly handles YAML files with multiple models through:

  • Content-based lookup using getModelNameInActiveEditor() which parses the YAML content of the active file
  • Fallback to filename-based lookup when content-based lookup fails
  • NodeMetaMap maintains proper mapping between model names and their metadata
🏁 Scripts executed

The following scripts were executed for the analysis:

Script:

#!/bin/bash
# Description: Check for YAML files with multiple model definitions

# Look for YAML files that might contain multiple model definitions
echo "Searching for YAML files with multiple model definitions..."
rg -l "^[[:space:]]*models?:" --type yaml | while read -r file; do
  echo "Analyzing $file..."
  # Count model definitions in each file
  rg -c "^[[:space:]]*name:[[:space:]]*" "$file"
done

# Look for potential model references in the codebase
echo "Checking model references in the codebase..."
ast-grep --pattern 'lookupByBaseName($_)'

Length of output: 330


Script:

#!/bin/bash
# Search for the implementation of lookupByBaseName and getModelNameInActiveEditor
echo "Checking lookupByBaseName implementation:"
ast-grep --pattern 'lookupByBaseName($_) {
  $$$
}'

echo "\nChecking getModelNameInActiveEditor implementation:"
ast-grep --pattern 'getModelNameInActiveEditor() {
  $$$
}'

# Check how YAML files are parsed
echo "\nChecking YAML parsing logic:"
rg -A 5 "parse.*yaml" -g "*.ts"

Length of output: 2173


Script:

#!/bin/bash
# Check for model extraction logic
echo "Checking model extraction from YAML:"
rg -A 10 "getModelNameInActiveEditor" -g "*.ts"

# Check NodeMetaMap implementation
echo "\nChecking NodeMetaMap implementation:"
rg -A 10 "class NodeMetaMap" -g "*.ts"

# Check for YAML model parsing
echo "\nChecking YAML model parsing:"
rg -A 10 "models:\s*" -g "*.ts"

Length of output: 15700

Comment on lines +112 to +130
const model_by_file_content = event.nodeMetaMap.lookupByBaseName(
getModelNameInActiveEditor(),
);
const model = event.nodeMetaMap.lookupByBaseName(fileName);
if (!model) {
return Promise.resolve([]);

if (model_by_file_content) {
return Promise.resolve(
this.getTreeItems(model_by_file_content.uniqueId, event),
);
}
return Promise.resolve(this.getTreeItems(model.uniqueId, event));

const fileName = path.parse(
window.activeTextEditor!.document.fileName,
).name;
const model_by_file_name = event.nodeMetaMap.lookupByBaseName(fileName);
if (model_by_file_name) {
return Promise.resolve(
this.getTreeItems(model_by_file_name.uniqueId, event),
);
}
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Extract shared model lookup logic to reduce code duplication

The model lookup logic is duplicated between ModelTreeviewProvider and DocumentationTreeviewProvider. Consider extracting it into a shared utility function.

Create a new utility function:

async function lookupModelByEditorContent(
  nodeMetaMap: NodeMetaMap,
  fileName: string
): Promise<Node | undefined> {
  try {
    // Try content-based lookup first
    const modelName = await getModelNameInActiveEditor();
    const model = nodeMetaMap.lookupByBaseName(modelName);
    if (model) return model;

    // Fall back to filename-based lookup
    return nodeMetaMap.lookupByBaseName(path.parse(fileName).name);
  } catch (error) {
    console.warn('Failed to lookup model:', error);
    return undefined;
  }
}

Then use it in both providers:

-   const model_by_file_content = event.nodeMetaMap.lookupByBaseName(
-     getModelNameInActiveEditor(),
-   );
-
-   if (model_by_file_content) {
-     return Promise.resolve(
-       this.getTreeItems(model_by_file_content.uniqueId, event),
-     );
-   }
-
-   const fileName = path.parse(
-     window.activeTextEditor!.document.fileName,
-   ).name;
-   const model_by_file_name = event.nodeMetaMap.lookupByBaseName(fileName);
-   if (model_by_file_name) {
-     return Promise.resolve(
-       this.getTreeItems(model_by_file_name.uniqueId, event),
-     );
-   }
+   const currentNode = await lookupModelByEditorContent(
+     event.nodeMetaMap,
+     window.activeTextEditor!.document.fileName
+   );
+   if (currentNode) {
+     return this.getTreeItems(currentNode.uniqueId, event);
+   }

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.

3 participants