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

FR: new Rule to capitalize bulleted list items #1239

Open
x-thompson3 opened this issue Dec 11, 2024 · 7 comments
Open

FR: new Rule to capitalize bulleted list items #1239

x-thompson3 opened this issue Dec 11, 2024 · 7 comments
Labels
rule suggestion Suggestion to add or edit a rule

Comments

@x-thompson3
Copy link

Is Your Feature Request Related to a Problem? Please Describe.

I'd love for Linter to be able to format bulleted lists so that the first word of the list is capitalized. It's clean, consistent, and saves some effort for mobile users of Obsidian.

Describe the Solution You'd Like

A new Rule created that, when enabled, capitalizes the first word of bulleted lists, regardless of indentation level. Perhaps needs an ignore-list for words like 'iPhone' and the like.

Please include an example where applicable:

Before:

# Session Notes
- the dungeon crawl continues!
- [[Sylas Voss]] nearly falls into a spike pit, but [[Howl]] luckily had Feather Fall stocked.
- a lucky Perception roll investigate leads [[Cesar]] to discover the trigger for the dart traps
- the group makes camp just outside the final ritual chamber
	- session ended with [[Gorethrandir]] doing push-ups to "to look good for the fight"

After:

# Session Notes
- The dungeon crawl continues!
- [[Sylas Voss]] nearly falls into a spike pit, but [[Howl]] luckily had Feather Fall stocked.
- A lucky Perception roll investigate leads [[Cesar]] to discover the trigger for the dart traps
- The group makes camp just outside the final ritual chamber
	- Session ended with [[Gorethrandir]] doing push-ups "to look good for the fight"

Describe Alternatives You've Considered

The Custom Regex option doesn't support the \U case modifier, which was my first attempted implementation.
I tried creating a QuickAdd Macro, but the documentation is a little sparse. Below is what I scraped together, which had two problems:

  • For Lint on Focused Field Change, the file that gets changed is the file that I switch to, instead of the one I'm leaving (presumably because I'm getting the content from params.app.workspace.activeLeaf.view.editor)
  • For both Lint on save and Lint on Focused File Change, this macro gets run twice somehow whenever Linter calls it. I haven't been able to figure out why.
// This function takes the current active file and capitalizes any lowercase letters that are at the start of an unordered (bulleted) list. 
// It relies on the Linter functionality of enforcing list types as '-' instead of '*' or '+'
module.exports = async (params) => {
    const editor = params.app.workspace.activeLeaf.view.editor;
    const currentContent = editor.getValue();
    console.log(params);
    const bulletedListRegex = /^\s*- [a-z]/gm;
    
    var numModified = 0;
    const newContent = currentContent.replace(bulletedListRegex, (match) => {
        numModified += 1;
        return match.toUpperCase();
    });

    editor.setValue(newContent);
    if (numModified > 0) {
        new Notice("Modified " + numModified + " bullets");
    } else {
        console.log("No files modified")
    }
};

Additional Context

None.

@x-thompson3 x-thompson3 added the rule suggestion Suggestion to add or edit a rule label Dec 11, 2024
@pjkaufman
Copy link
Collaborator

@x-thompson3 , could you elaborate a little on what you mean by custom regex not supporting \U? Is that due to not having browser support or has that functionality been baked in? If it has not been baked in, I can see about fixing that since custom regex should emulate a normal regex in JS.

@pjkaufman pjkaufman added the need more info This issue needs more info on it in order to be worked on label Dec 24, 2024
@x-thompson3
Copy link
Author

@pjkaufman here's an example note:

---
MOC: 
aliases:
  - lowercase alias that would ideally get skipped
---
# Heading Title
- [[Link to other note]] was used
- a bullet with no indents
# Another heading
- [[a note with a lowercase name]] should not have anything capitalized
- items with any number of indents should still be capitalized
	- bulleted list item with indents
		- indented even further! How impressive
- iOS ideally isn't supposed to be capitalized, but I don't think there's a clean regex way to stop that

If I set a custom regex rule as ^(\s*)- ([a-z])([A-Za-z]*)\s, gm, $1- \U$2\E$3 , then this is the result when I run the Lint Current File command:

---
MOC: 
aliases:
  - \Ul\Eowercase alias that would ideally get skipped
---
# Heading Title
- [[Link to other note]] was used
- \Ua\E bullet with no indents
# Another heading
- [[a note with a lowercase name]] should not have anything capitalized
- \Ui\Etems with any number of indents should still be capitalized
	- \Ub\Eulleted list item with indents
		- \Ui\Endented even further! How impressive
- \Ui\EOS ideally isn't supposed to be capitalized, but I don't think there's a clean regex way to stop that

Whereas it to works as expected with regex101.com:
regex101 shows slash-U and slash-E for uppercase replacement

I also realized that if this capitalization was done solely using the custom regex, there might be edge cases where the capitalization is undesirable (iOS and similar lower-case proper nouns, frontmatter list-type properties). An actual Linter rule is probably the preferred solution to make use of an ignore-list.

@pjkaufman
Copy link
Collaborator

I do agree that to handle edge cases like exceptions to words to capitalize it would be best to have a program handle the capitalization to fine tune things, but I am not sure if the Linter is the right place for this. It could be neat, but as it stands it seems niche to me.

It may be something that users want, but at this time I see little indication of that. For now, I can leave this open and see if any other users would like this feature and close it if it is not something users would like.

@pjkaufman pjkaufman removed the need more info This issue needs more info on it in order to be worked on label Jan 5, 2025
@magi44ken
Copy link

@pjkaufman here's an example note:

---
MOC: 
aliases:
  - lowercase alias that would ideally get skipped
---
# Heading Title
- [[Link to other note]] was used
- a bullet with no indents
# Another heading
- [[a note with a lowercase name]] should not have anything capitalized
- items with any number of indents should still be capitalized
	- bulleted list item with indents
		- indented even further! How impressive
- iOS ideally isn't supposed to be capitalized, but I don't think there's a clean regex way to stop that

If I set a custom regex rule as ^(\s*)- ([a-z])([A-Za-z]*)\s, gm, $1- \U$2\E$3 , then this is the result when I run the Lint Current File command:

---
MOC: 
aliases:
  - \Ul\Eowercase alias that would ideally get skipped
---
# Heading Title
- [[Link to other note]] was used
- \Ua\E bullet with no indents
# Another heading
- [[a note with a lowercase name]] should not have anything capitalized
- \Ui\Etems with any number of indents should still be capitalized
	- \Ub\Eulleted list item with indents
		- \Ui\Endented even further! How impressive
- \Ui\EOS ideally isn't supposed to be capitalized, but I don't think there's a clean regex way to stop that

Whereas it to works as expected with regex101.com: regex101 shows slash-U and slash-E for uppercase replacement

I also realized that if this capitalization was done solely using the custom regex, there might be edge cases where the capitalization is undesirable (iOS and similar lower-case proper nouns, frontmatter list-type properties). An actual Linter rule is probably the preferred solution to make use of an ignore-list.

I'm new to the Linter plugin. Can you explain how to enter this code in the Linter plugin? I also wanted to capitalize the first letter of the first word in a list. Thanks.

@x-thompson3
Copy link
Author

x-thompson3 commented Jan 7, 2025

@magi44ken and anyone else interested in the implementation I ended up using:

Caveats

  • The JS code does not distinguish the note's frontmatter from its contents, so it will capitalize the values in list-type Properties if they're not in the ignore list. Modify the ignoredLowercaseWords list in my code as you need.
  • For Lint on Focused File Change, the file that gets changed is the file that I switch to, instead of the one I'm leaving. This is because I'm getting the contents of the note from params.app.workspace.activeLeaf.view.editor, instead of however Linter specifies which note is being linted. I couldn't figure out how to get the reference to the note that Linter is targeting, but I switch between my notes frequently enough that it doesn't make much of a difference for me.
  • Running the code via the QuickAdd Macro or Lint this file on the currently-open note works as expected, and I have not tested whether it works with Lint all files in the vault or similar commands - I expect it will fail but I don't know what the result will be.
  • For both Lint on save and Lint on Focused File Change, the macro gets run twice for some reason. I haven't been able to figure out why, but I included a catch to only save the contents to the note if there were actual modifications being made.

@pjkaufman
Copy link
Collaborator

@pjkaufman here's an example note:

---
MOC: 
aliases:
  - lowercase alias that would ideally get skipped
---
# Heading Title
- [[Link to other note]] was used
- a bullet with no indents
# Another heading
- [[a note with a lowercase name]] should not have anything capitalized
- items with any number of indents should still be capitalized
	- bulleted list item with indents
		- indented even further! How impressive
- iOS ideally isn't supposed to be capitalized, but I don't think there's a clean regex way to stop that

If I set a custom regex rule as ^(\s*)- ([a-z])([A-Za-z]*)\s, gm, $1- \U$2\E$3 , then this is the result when I run the Lint Current File command:

---
MOC: 
aliases:
  - \Ul\Eowercase alias that would ideally get skipped
---
# Heading Title
- [[Link to other note]] was used
- \Ua\E bullet with no indents
# Another heading
- [[a note with a lowercase name]] should not have anything capitalized
- \Ui\Etems with any number of indents should still be capitalized
	- \Ub\Eulleted list item with indents
		- \Ui\Endented even further! How impressive
- \Ui\EOS ideally isn't supposed to be capitalized, but I don't think there's a clean regex way to stop that

Whereas it to works as expected with regex101.com: regex101 shows slash-U and slash-E for uppercase replacement
I also realized that if this capitalization was done solely using the custom regex, there might be edge cases where the capitalization is undesirable (iOS and similar lower-case proper nouns, frontmatter list-type properties). An actual Linter rule is probably the preferred solution to make use of an ignore-list.

I'm new to the Linter plugin. Can you explain how to enter this code in the Linter plugin? I also wanted to capitalize the first letter of the first word in a list. Thanks.

@magi44ken , to add a rule to the Linter, I would recommend reading the documentation on adding a rule located here. You would definitely want to use the helper function for editing list item text called updateListItemText. It is used a couple of places for updating the text of a list item. Hopefully this helps in at least getting started. I will try my best to make time to answer questions as they come up.

@pjkaufman
Copy link
Collaborator

@x-thompson3 , the Linter internally keeps track of the last active leaf on file change. I don't believe that is accessible outside of the plugin. It is located on a private property called lastActiveFile. You probably cannot access it directly, but you can always try. Also, the macro might work for linting all files in a vault/folder, but it would depend on some logic that has not been proven to work in the Linter. But it in theory would work. I just would not rely on it working.

The reason the logic runs twice for the macro when you have Lint on File Change (content change) enabled is because the Linter has no idea what changes are made by custom commands or even when they finish with certainty (this is the logic that I mentioned before that has not been thoroughly tested, but tries its best). So the Linter sees the change made by the Macro as Obsidian or a user change. Thus it kicks off the Linter logic again. It is potentially possible to ignore the custom command logic for Linter changes, but that may be bugging and cause unexpected results since the Linter has no control over it.

Hopefully this helps better understand some of the limitations you hit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
rule suggestion Suggestion to add or edit a rule
Projects
None yet
Development

No branches or pull requests

3 participants