Carleslc / Simple-YAML

This Java API provides an easy-to-use way to store data and provide configurations using the YAML format.
https://carleslc.me/Simple-YAML
GNU General Public License v3.0
132 stars 38 forks source link

How can I parse/save header/comment correctly? #53

Closed re-ovo closed 2 years ago

re-ovo commented 2 years ago

Suppose I have a yaml config:

# Example Config
# Generated by {version}

# check update
check_update: true

# another option
option: 1

yaml.option().header() will return:

# Example Config
# Generated by {version}

# check update

This is obviously not what I need 🤔

yaml.getComment("check_update") also returns

# Example Config
# Generated by {version}

# check update

Is there any way to separate them?

re-ovo commented 2 years ago

Also, when I get/set a comment, the empty comment lines and blank lines are removed, is there any way to keep it?

For example:

after use

  yaml.setComment("option", "New Comment")

the config will become:

# Example Config
# Generated by {version}

# check update
check_update: true
# New Comment
option: 1

Apparently, the blank line above New Comment was removed.

Also, it also strips my comment formatting, for example:

############### Example #####################
#
# Test
#
###########################################

it will become:

# Example
# Test
Carleslc commented 2 years ago

Currently, the header is considered to be the block comment of the first entry (root), without stripping #. But I guess you need the header to be something like all comments before the first blank line.

You can get the header you want with something like:

yaml.options().header().split("\n\n")[0]

The current header behaviour is mostly the same as the original Bukkit YamlConfiguration. But maybe some rework can be done to achieve this most intuitive behaviour.

re-ovo commented 2 years ago

Currently, the header is considered to be the block comment of the first entry (root), without stripping #. But I guess you need the header to be something like all comments before the first line without a comment prefix #.

You can get the header you want with something like:

yaml.options().header().split("\n(?!#)")[0]

The current header behaviour is mostly the same as the original Bukkit YamlConfiguration. But maybe some rework can be done to achieve this most intuitive behaviour.

thanks, how about the comment issue?

Carleslc commented 2 years ago

I think it is not possible at the moment to add blank lines along with a comment programmatically. Current blank lines that exist in the file will be preserved untouched, but if you use setComment to add comments the formatting is still something to work on.

Currently, the formatted comments like the one you provide are supposed to be written on the default file manually and not programmatically using setComment method, as they will not be modified.

You can set blank lines as a comment with yaml.setComment("option", "\n"), but if you include the \n character along with text:

yaml.setComment("option", "\nNew Comment");

The output will be:

# Example Config
# Generated by {version}

# check update
check_update: true
#
# New Comment
option: 1

If the comment begins with a \n it would need to be a blank line to achieve what you're trying to do. It is a presentation detail, but given that comments need some fixes as stated in #42 then probably this will be added too.

Currently adding a \n character in a comment is to write multi-line comments (#39):

yaml.setComment("option", "One line\nAnother line");
# Example Config
# Generated by {version}

# check update
check_update: true
# One line
# Another line
option: 1

getComment will give you those comments without the # character, and this is ok for the comment prefixes above because they have no real meaning in the comment content, but if the text contains # then they would need to be preserved, and currently they are stripped away. It is a known bug and this is something that will change in the next release.

Carleslc commented 2 years ago

The 1.8 release includes a comment formatting feature useful to control how you want to format and retrieve comments. Also, the header behaviour changed, and now it is separated from the first key comment by the first blank line found.

This is how to do what you want after upgrading:

// Create YamlFile with relative path
final YamlFile yamlFile = new YamlFile("issue-53.yml");

// Load file with comments
try {
    yamlFile.createOrLoadWithComments();
} catch (IOException e) {
    e.printStackTrace();
}

// Set header format
yamlFile.options().headerFormatter()
        .prefixFirst("############### Example #####################\n#")
        .suffixLast("\n#\n#############################################");

// Set header
yamlFile.options().header("Example Config\nGenerated by {version}");

// Set comment formatter
// DEFAULT (unset) does not add any blank line
// PRETTY will add a blank line above comments at root keys (those with indent 0)
yamlFile.setCommentFormat(YamlCommentFormat.PRETTY);

// Add values along with comments using the alternative API (path method)
yamlFile.path("check_update").set(true).comment("check update");

yamlFile.path("option").set(1).comment("another option");

// Get header
System.out.println("Header without format: \n\n" + yamlFile.options().header());
System.out.println();
System.out.println("Header with format: \n\n" + yamlFile.getHeader());
System.out.println();

// Get first comment
System.out.println("check_update comment: " + yamlFile.getComment("check_update"));

// Save file
try {
    yamlFile.save();
} catch (IOException e) {
    e.printStackTrace();
}

Output

Header without format: 

Example Config
Generated by {version}

Header with format: 

############### Example #####################
#
# Example Config
# Generated by {version}
#
#############################################

check_update comment: check update

issue-53.yml

############### Example #####################
#
# Example Config
# Generated by {version}
#
#############################################

# check update
check_update: true

# another option
option: 1

Another way to set blank lines (explicitly):

// Create YamlFile with relative path
final YamlFile yamlFile = new YamlFile("issue-53-2.yml");

// Load file with comments
try {
    yamlFile.createOrLoadWithComments();
} catch (IOException e) {
    e.printStackTrace();
}

// If no comment format is set, it is equivalent to:
// yamlFile.setCommentFormat(YamlCommentFormat.DEFAULT);

// Add some values
yamlFile.set("option-1", 1);
yamlFile.set("option-2", 2);
yamlFile.set("nested.option-3", 3);
yamlFile.set("nested.option-4", 4);

// This does not add any blank line (DEFAULT format)
yamlFile.setComment("option-1", "DEFAULT Comment");

// If each comment line is prefixed with # then the default formatter is ignored (RAW format)
yamlFile.setComment("option-2", "\n# RAW Comment");

// Add a blank line without comment
yamlFile.setBlankLine("nested");

// This is a multiline comment, both lines with # prefix (DEFAULT format)
yamlFile.setComment("nested.option-3", "\nDEFAULT Comment with new line");

// This will add a blank line above (BLANK_LINE format)
yamlFile.setComment("nested.option-4", "BLANK_LINE Comment", YamlCommentFormat.BLANK_LINE);

// Save file
try {
    yamlFile.save();
} catch (IOException e) {
    e.printStackTrace();
}

issue-53-2.yml

# DEFAULT Comment
option-1: 1

# RAW Comment
option-2: 2

nested:
  # 
  # DEFAULT Comment with new line
  option-3: 3

  # BLANK_LINE Comment
  option-4: 4

More information:

Another example with custom comment formatting with custom prefix and suffix:

Carleslc commented 2 years ago

I'm closing this issue as I think is resolved. If you have any question or encounter a related problem with this issue feel free to ask or reopen it.