-
-
Notifications
You must be signed in to change notification settings - Fork 15
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
Added codeAction (extract subSchema to defs) #133
base: main
Are you sure you want to change the base?
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is great progress! But, it looks like there's still some work to do.
Adding the definitions to the top isn't good enough. Also, the sloppy formatting of the generated code isn't acceptable either. The biggest motivating factor for this project is to encourage best practices and good style. Two of those rules are that $schema
and $id
always come first and that $defs
always goes last. It's important that the definitions are in the right place and reasonably formatted when moved. You might have to somehow run the moved code through a formatter once it's in its new location. jsonc-parser is used internally to parse the schema. It has a formatting feature that you might be able to use somehow.
I noticed that the schema you're using in your demo video has a problem. The "shipping_address" property has a property "address" that then has the actual schema in it. That subschema will do nothing at all. "address" is not JSON Schema keyword, so it gets ignored. You can tell something's wrong because the schema in "address" doesn't have the expected syntax highlighting. JSON Schema keywords should be highlighted differently than plain JSON properties.
I see you're using the property name as the definition name. That's a nice default, but that's not always going to work. Subschemas can be in quite a few places other than properties
values. For example, this would work for the items
keyword. I think what needs to happen is that the user needs to provide the definition name. Have a look at the if
/then
completion provider. There's a special syntax that allows you to set placeholders that the user will be prompted to fill in. That approach may also work here.
You'll need to add tests for this feature that cover whatever edge cases you can think of.
I appreciate the clean and well written code so far. I suggest installing the ESLint plugin in your editor to get linting feedback while developing. It will help avoid getting build errors in your PRs if you forget to run the linter before pushing.
Thank you for the detailed feedback. However, I encountered an issue while looking into what you mentioned:
I found that |
Thanks for figuring that out. Yes, it looks like SnippetTextEdit is what we need. Although 3.18 isn't released yet, it looks like |
I think it's not supported by As you can see in the screenshots, the latest available version is still 3.17.5, and this feature is introduced in 3.18. I also checked the nodeModule Screenshot
![]() |
Yes, you're right. Although the code was merged over a year ago, it hasn't made it to an official release yet. It's planned for the next major release (v10) and that only has pre-releases published so far. We could change the dependency to `"vscode-languageserver": "^10.0.0-next" to use the pre-release. But, that's only the server. It probably wouldn't work in vscode yet. I guess that means we have to hold off on that detail. I was hoping this would give us a way to avoid having to come up with a more robust way to generate definition names, but it looks like we're going to have to find a temporary solution until this feature is released. |
I think, for now, we should keep it simple and implement a basic defCounter that generates definition names like def1, def2, and so on. Do you have any other solutions in mind? Please let me know your thoughts. |
Number based naming can work, but there are some some edge cases. Extract a schema and you'd get Also, if you do refactorings in one schema and then go to another schema, it would be weird for it to generate One thing that I think would work is the generate number-based names starting at Or, you could inspect all the definition names looking for the Or, you could generate a UUID and use that as the name and not have to check anything. But, a bulky UUID might not make for as good a user experience. Any of those options would be ok. |
Alright, I’ll work on it and let you know once it’s done. |
Screen.Recording.2025-02-24.at.12.48.49.AM.movPlease review it. If everything looks good, I will start writing the test cases. |
When switching the dialect URI of the schema, we need to change |
Use the |
Got it! I'll use |
I just made the change, you can check it now. I'll write the test case as soon as possible. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Have another try at the JSON formatting part. The other things I mentioned should be small and easy to address. I included a few code style suggestions that aren't caught by the linter. In general, I thought the code made better use of whitespace the last time I reviewed. Things are more compact now making the code harder to read.
I have used a library to detect the indentation of the current file correctly, and now everything is working fine. I also added this condition In the current CodeAction logic, it finds the parent definition node and places the new definition property at the beginning. However, we could modify it to find the nearest definition node and place the new definition property there. While this approach would be more precise, it would also make the implementation slightly more complex. Additionally, we need to determine the spaces before the $defs and align them with the user's indentation to maintain consistency in formatting. Additionally, in the if put only this Screen.Recording.2025-02-27.at.3.07.38.AM.movwith this Screen.Recording.2025-02-27.at.3.08.26.AM.movIf you have any better ideas, please share. |
4c41d08
to
5386d6e
Compare
Your problem is that you're formatting the extracted definition in isolation. The idea is to format the definition within the context of the full JSON document. That context is how it knows how much to indent. The the edits the import { applyEdits, format } from "jsonc-parser";
/**
* @import { FormattingOptions, Edit } from "jsonc-parser"
*/
/** @type (text: string, edit: Edit, options: FormattingOptions) => Edit */
const withFormatting = (text, edit, options) => {
const newText = applyEdits(text, [edit]);
const range = { offset: edit.offset, length: edit.content.length };
const formatEdits = format(newText, range, options);
// Adjust the offsets so they can be applied to just the definition
for (const formatEdit of formatEdits) {
formatEdit.offset -= edit.offset;
}
return { ...edit, content: applyEdits(edit.content, formatEdits) };
};
const schema = `{
"type": "object",
"properties": {
"foo": {
"type": "number"
},
"bar": { "$ref": "#/$defs/bar" }
},
"$defs": {
"bar": {
"type": "boolean"
}
}
}`;
const offset = 51;
const length = 30;
const definition = schema.substring(offset, offset + length);
const formattingOptions = {
keepLines: true,
insertSpaces: true,
tabSize: 2,
eol: "\n"
};
/** @type Edit[] */
const edits = [
{
offset: 51,
length: 30,
content: `{ "$ref": "#/$defs/def1" }`
},
withFormatting(schema, {
offset: 137,
length: 0,
content: `\n"def1": ${definition},`
}, formattingOptions)
];
console.log(edits);
console.log(applyEdits(schema, edits)); You should be able to do something similar, but your version of the |
Thanks for the insight! |
The definition should go at the root of the schema resource it's in. A schema resource is a schema with |
now it is working fine with all the correct indentation.
Screen.Recording.2025-03-02.at.7.28.01.PM.mov
Screen.Recording.2025-03-02.at.7.29.10.PM.mov
Screen.Recording.2025-03-02.at.7.32.42.PM.mov |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Please remove .DS_Store
. It should be in .gitignore
if you would like to add it.
The formatting code is sloppy. Please try to clean that up. For example, there's no reason to pass textDocument
. You only use it to get the uri
which you use to read the file, but you already have the text, so there's no need to read the file. It's all more complicated than it needs to be and needs more thought.
I have made the changes. Could you please check? |
I did some clean up of the formatting code. Have a look. |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Let's have another look at the Configuration changes and try to clean things up. I noticed there was some duplication in that configs were being built in two places. I handled that problem, so you don't have to worry about it, but let's see if we can make the rest a little nicer.
That's a lot of learning from you! I can clearly see the difference between my approach and yours in the |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There are a few issues with the types.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
It's looking much better now! I pushed a little more clean up for the Configuration class and I updated the TestClient. It originally only supported the one configuration scope. Now it can support others. You'll need this when you write tests.
One more change I'd like to see is that I want the definition to get added to the bottom of $defs
, not the top. Then go ahead with writing tests.
Thanks for sticking with this. I told you when you took this on that it could be a tough one. The hardest part is often writing the tests, so good luck as you move into that phase.
Okay i'll do it,
Thanks for your guidance. It’s been a challenging task, but also a great learning experience for me! |
The easiest approach I found is using |
|
76baef3
to
7e2da01
Compare
I wanted to point out an edge case, if the Screen.Recording.2025-03-15.at.12.42.23.AM.mov |
That's definitely something we'll want to have a test for. |
#132
Point to Note in this:-
Screen.Recording.2025-02-20.at.10.47.01.PM.mov