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

👍 implement options method with class #266

Merged
merged 3 commits into from
Aug 29, 2024
Merged

👍 implement options method with class #266

merged 3 commits into from
Aug 29, 2024

Conversation

Milly
Copy link
Contributor

@Milly Milly commented Aug 28, 2024

  • Improve logic maintainability.
  • Minimize code generation logic.
  • Reduce the module size. (888KB to 492KB in option/**)

Breaking

This change breaks codes like below.
But I think these can be included in the specifications as unexpected uses.:

import * as opOld from "jsr:@denops/[email protected]/option";
import * as op from "jsr:@denops/std/option";

declare const denops: Denops;

const getDictionaryOld = opOld.dictionary.get;
await getDictionaryOld(denops); // OK

const getDictionaryNew = op.dictionary.get;
await getDictionaryNew(denops); // TypeError: Cannot read properties of undefined (reading '#name')

const getDictionarySafe = op.dictionary.get.bind(op.dictionary);
await getDictionarySafe(denops); // OK
import * as op from "jsr:@denops/std/option";

const options = [
  op.diff,
  op.diffexpr,
];
const localOptions = options.filter(
  (option) => typeof (option as any).getLocal === "function"
); // expected [op.diff] but actual is [op.diff, op.diffexpr]

Summary by CodeRabbit

  • New Features

    • Introduced a streamlined approach for managing options, enhancing maintainability and clarity.
    • Added specific classes for Boolean, Number, and String options, improving usability.
  • Refactor

    • Simplified option formatting and instantiation logic, reducing complexity and boilerplate code.
    • Updated option declarations to leverage the new utility classes for a cleaner codebase.
  • Bug Fixes

    • Enhanced type safety in option management by utilizing TypeScript's type system effectively.

Copy link

coderabbitai bot commented Aug 28, 2024

Walkthrough

The changes involve a significant refactor of option handling in a Denops environment. Key modifications include the removal of multiple functions related to option formatting, the introduction of a new utility class structure for managing options, and the instantiation of options using specific classes such as BooleanOption, NumberOption, and StringOption. This restructuring aims to streamline the management and instantiation of options across different scopes.

Changes

Files Change Summary
.scripts/gen-option/format.ts, option/nvim/_generated.ts Removed several option formatting functions and replaced them with a new function for option instantiation. Adjusted option declarations to utilize new utility classes for improved structure.
option/_utils.ts Introduced an abstract class OptionImpl for managing options and added concrete classes for specific option types, enhancing type safety and reducing boilerplate.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant FormatFunction
    participant OptionConstructor

    User->>FormatFunction: Request to format options
    FormatFunction->>OptionConstructor: Call getOptionConstructor()
    OptionConstructor-->>FormatFunction: Return new option instance
    FormatFunction-->>User: Return formatted options
Loading

🐰 In a world of options, bright and new,
A rabbit hops with joy, it's true!
With classes neat and functions few,
We streamline paths, all shiny and blue.
Let's celebrate this change today,
In the Denops garden, we frolic and play! 🌼


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>.
    • 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 generate interesting stats about this repository and render them as a table.
    • @coderabbitai show all the console.log statements in this repository.
    • @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 as 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 resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Additionally, you can add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.

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

@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, codebase verification and nitpick comments (2)
.scripts/gen-option/format.ts (1)

39-51: Refactor approved, but ensure error handling.

The getOptionConstructor function introduces a modular approach by using specific classes for different option types. Ensure that the calling code properly handles the potential error thrown by the default case to avoid runtime exceptions.

The code changes are approved.

Consider adding error handling in the calling code to manage the exceptions thrown by this function effectively.

option/_utils.ts (1)

13-35: Class implementation approved, suggest enhancing error handling.

The OptionImpl class provides a robust structure for option handling with type safety through coercion functions. Consider enhancing error handling for asynchronous operations to manage potential exceptions more effectively.

The code changes are approved.

Consider implementing try-catch blocks around asynchronous operations to handle exceptions gracefully.

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 016970e and 5570305.

Files selected for processing (3)
  • .scripts/gen-option/format.ts (3 hunks)
  • option/_utils.ts (1 hunks)
  • option/nvim/_generated.ts (15 hunks)
Additional comments not posted (20)
.scripts/gen-option/format.ts (4)

8-12: LGTM!

The formatDocs function correctly formats documentation strings.

The code changes are approved.


Line range hint 25-31: LGTM!

The getOptionTypeName function correctly determines the type name based on the option's scope.

The code changes are approved.


56-60: Refactor approved for improved maintainability.

The format function effectively integrates the new changes, using flat mapping to simplify the generation of formatted lines. This enhances maintainability and clarity.

The code changes are approved.


21-23: Refactor approved, but verify the translation dictionary.

The refactoring of formatOption to use getOptionConstructor simplifies the code. However, ensure that the translate dictionary is correctly maintained and covers all necessary cases.

The code changes are approved.

Run the following script to verify the translation dictionary usage:

option/_utils.ts (2)

82-86: Specialization for boolean options approved.

The BooleanOption class is correctly implemented, extending OptionImpl with appropriate settings for boolean options.

The code changes are approved.


88-98: Specializations for number and string options approved.

The NumberOption and StringOption classes are correctly implemented, extending OptionImpl with appropriate settings for their respective types.

The code changes are approved.

option/nvim/_generated.ts (14)

16-16: Refactor to NumberOption for channel approved.

The implementation using NumberOption simplifies handling and is correctly set up for a local option of type number.

The code changes are approved.


35-35: Refactor to StringOption for inccommand approved.

The implementation using StringOption simplifies handling and is correctly set up for a global option of type string.

The code changes are approved.


60-62: Refactor to StringOption for mousescroll approved.

The implementation using StringOption simplifies handling and is correctly set up for a global option of type string.

The code changes are approved.


80-80: Refactor to NumberOption for pumblend approved.

The implementation using NumberOption simplifies handling and is correctly set up for a global option of type number.

The code changes are approved.


116-118: Refactor to StringOption for redrawdebug approved.

The implementation using StringOption simplifies handling and is correctly set up for a global option of type string.

The code changes are approved.


128-128: Refactor to NumberOption for scrollback approved.

The implementation using NumberOption simplifies handling and is correctly set up for a local option of type number.

The code changes are approved.


247-247: Refactor to StringOption for shada approved.

The implementation using StringOption simplifies handling and is correctly set up for a global option of type string.

The code changes are approved.


259-259: Refactor to StringOption for shadafile approved.

The implementation using StringOption simplifies handling and is correctly set up for a global option of type string.

The code changes are approved.


323-325: Refactor to StringOption for statuscolumn approved.

The implementation using StringOption simplifies handling and is correctly set up for a local option of type string.

The code changes are approved.


349-351: Refactor to StringOption for termpastefilter approved.

The implementation using StringOption simplifies handling and is correctly set up for a global option of type string.

The code changes are approved.


361-361: Refactor to BooleanOption for termsync approved.

The implementation using BooleanOption simplifies handling and is correctly set up for a global option of type boolean.

The code changes are approved.


380-380: Refactor to StringOption for winbar approved.

The implementation using StringOption simplifies handling and is correctly set up for a global or local option of type string.

The code changes are approved.


391-391: Refactor to NumberOption for winblend approved.

The implementation using NumberOption simplifies handling and is correctly set up for a local option of type number.

The code changes are approved.


414-416: Refactor to StringOption for winhighlight approved.

The implementation using StringOption simplifies handling and is correctly set up for a local option of type string.

The code changes are approved.

Copy link

codecov bot commented Aug 28, 2024

Codecov Report

All modified and coverable lines are covered by tests ✅

Project coverage is 84.63%. Comparing base (016970e) to head (9a141c3).
Report is 4 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #266      +/-   ##
==========================================
+ Coverage   84.23%   84.63%   +0.39%     
==========================================
  Files          62       63       +1     
  Lines        3292     3377      +85     
  Branches      291      291              
==========================================
+ Hits         2773     2858      +85     
  Misses        517      517              
  Partials        2        2              

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

@lambdalisue
Copy link
Member

lambdalisue commented Aug 28, 2024

We should avoid breaking changes unless there are significant benefits for both developers and users. However, I believe this PR does not offer enough value for users.

Pros

  • Improve logic maintainability.
  • Minimize code generation logic.
  • Reduce the module size. (888KB to 492KB in option/**)

Cons

  • Users may access options in an unintended way
    • For example, winfixwidth is meant to be window-local, but users can still access it through the getBuffer method.

Therefore, I think we should not accept this PR. Did I misunderstand your perspective?

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

Review details

Configuration used: CodeRabbit UI
Review profile: CHILL

Commits

Files that changed from the base of the PR and between 5570305 and 9a141c3.

Files selected for processing (3)
  • .scripts/gen-option/format.ts (3 hunks)
  • option/_utils.ts (1 hunks)
  • option/nvim/_generated.ts (15 hunks)
Files skipped from review as they are similar to previous changes (2)
  • option/_utils.ts
  • option/nvim/_generated.ts
Additional comments not posted (2)
.scripts/gen-option/format.ts (2)

39-51: Implement a switch-case for option type instantiation.

This new function, getOptionConstructor, uses a switch-case to instantiate different types of options based on the type parameter. This approach is clean and straightforward, making it easy to understand and maintain.

However, the use of a never type in line 48 as a fallback for unknown types is a good practice to ensure type safety. This ensures that the function will fail at runtime if an unsupported type is passed, which can help catch errors early during development.

The implementation here is robust and adheres to good practices in TypeScript for ensuring type safety and clarity.


56-60: Update imports to reflect new utility usage.

The import statements have been updated to reflect the new structure and dependencies, specifically importing the BooleanOption, NumberOption, and StringOption from a utility module. This change supports the new object-oriented approach to handling options.

The changes to the imports are appropriate and align with the new architectural approach. Ensure that the utility module is correctly implemented and accessible from this path to avoid module resolution errors.

Comment on lines +21 to +23
`export const ${name}: ${getOptionTypeName(scope, type)} = ${
getOptionConstructor(name, type)
};`,
Copy link

Choose a reason for hiding this comment

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

Refactor to use dynamic option instantiation.

The refactoring to use getOptionConstructor for creating option instances simplifies the code by removing multiple conditional checks and function calls that were previously used for this purpose. This change should enhance maintainability and clarity as it centralizes the instantiation logic.

However, as noted in the PR comments, this change introduces a breaking change by altering how options are accessed and instantiated. It is crucial to ensure that this new method does not lead to TypeError when properties are accessed on undefined, which has been a concern with the new implementation.

Ensure thorough testing and documentation are provided to guide developers on the new usage patterns to prevent runtime errors.

@Milly
Copy link
Contributor Author

Milly commented Aug 28, 2024

Cons

  • Users may access options in an unintended way

    • For example, winfixwidth is meant to be window-local, but users can still access it through the getBuffer method.

Therefore, I think we should not accept this PR. Did I misunderstand your perspective?

There are two problems:

  1. getBuffer() method exists even though winfixwidth etc are "local to window":
    This is an existing problem, it was there before this PR.
  2. getLocal() method etc exists in "global" option:
    This PR's problem.

All methods exist for all options (global or local), but not for the public types.
In other words, any method that is not exposed on the type is said to be @internal.
User (developer) can intentionally ignore types to "access options in an unintented way".

import * as op from "jsr:@denops/std/option";

declare const denops: Denops;

op.ambiwidth.getLocal(denops); // TS2551 [ERROR]: Property 'getLocal' does not exist on type 'GlobalOption<string>'.

(op.ambiwidth as any).getLocal(denops); // No error.

@lambdalisue
Copy link
Member

lambdalisue commented Aug 28, 2024

getBuffer() method exists even though winfixwidth etc are "local to window":
This is an existing problem, it was there before this PR.

Oops. I didn't notice that.

All methods exist for all options (global or local), but not for the public types.
In other words, any method that is not exposed on the type is said to be @internal.
User (developer) can intentionally ignore types to "access options in an unintented way".

Ah, I see. Then it's OK.


So this change is breaking ONLY for case 3 right?

Case 1

import * as op from "jsr:@denops/std/option";

console.log(await op.ambiwidth.get(denops));

Case 2

import { ambiwidth } from "jsr:@denops/std/option";

console.log(await ambiwidth.get(denops));

Case 3

import { ambiwidth } from "jsr:@denops/std/option";

const getAmbiwidth = ambiwidth.get;
//const getAmbiwidth = ambiwidth.get.bind(ambiwidth); // Must be like this
console.log(await getAmbiwidth(denops));

@Milly
Copy link
Contributor Author

Milly commented Aug 28, 2024

So this change is breaking ONLY for case 3 right?

Case 3

import { ambiwidth } from "jsr:@denops/std/option";

const getAmbiwidth = ambiwidth.get;
//const getAmbiwidth = ambiwidth.get.bind(ambiwidth); // Must be like this
console.log(await getAmbiwidth(denops));

Yes, ONLY for case 3 has the breaking change.

@Milly
Copy link
Contributor Author

Milly commented Aug 29, 2024

I'm working #267 and #268 as a continuation of this PR.
But it is a separate issue so I want to meke it a separate PR.

@Milly Milly requested a review from lambdalisue August 29, 2024 00:51
@lambdalisue lambdalisue merged commit 2ac1b5a into main Aug 29, 2024
10 checks passed
@lambdalisue lambdalisue deleted the option-class branch August 29, 2024 23:17
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