-
-
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?
Changes from all commits
f298838
2e9930e
77808f4
1845509
6ac18c5
a135b7b
62e5463
f87c1b2
ac7909a
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,115 @@ | ||
import { | ||
CodeActionKind, | ||
TextDocumentEdit | ||
} from "vscode-languageserver"; | ||
import { getKeywordName } from "@hyperjump/json-schema/experimental"; | ||
import * as SchemaDocument from "../../model/schema-document.js"; | ||
import * as SchemaNode from "../../model/schema-node.js"; | ||
import * as jsoncParser from "jsonc-parser"; | ||
|
||
/** | ||
* @import { Server } from "../../services/server.js" | ||
* @import { Schemas } from "../../services/schemas.js" | ||
* @import { CodeAction } from "vscode-languageserver"; | ||
*/ | ||
|
||
|
||
export class ExtractSubSchemaToDefs { | ||
/** | ||
* @param {Server} server | ||
* @param {Schemas} schemas | ||
*/ | ||
constructor(server, schemas) { | ||
this.server = server; | ||
this.schemas = schemas; | ||
server.onInitialize(() => ({ | ||
capabilities: { | ||
codeActionProvider: true | ||
} | ||
})); | ||
|
||
// Helper function to format new def using jsonc-parser | ||
/** @type (newDefText: string) => string */ | ||
const formatNewDef = (newDefText) => { | ||
try { | ||
/** @type {unknown} */ | ||
const parsedDef = jsoncParser.parse(newDefText); | ||
return JSON.stringify(parsedDef, null, 2).replace(/\n/g, "\n "); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Hardcoding the indentation strategy to two spaces isn't going to work. You're going to need to determine what indentation strategy the client is using and match that. You should be able to get the information from the configuration service, but it's currently only configured to retrieve this server's configs. I think there's another "section" you'd have to request, but you'll have to figure out what that is. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have made all the changes, but I am stuck on the formatting part. It always takes the default tab size of four instead of the current tab size. Even though I am fetching the editor settings, it doesn't seem to reflect the actual tab size used in the document. Do you have any suggestions on how to correctly retrieve the active document's indentation settings? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Try sending the document's URI ( I think it should be ok to add an optional parameter to the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I also tried this approach, but when I console it in the terminal, it still always shows 4. Not sure why it's not picking up the actual tab size. Any other suggestions? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't have any more guesses. I'll try to find some time tonight to try some things and see if I can figure anything out. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Here's what I figured out. I think getting 4 is technically correct. Your editor is configured for a 4 space indentation by default. However, vscode also has a setting called "Detect Indentation". If that is set, it ignores the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Okay, then I'll set detectIndentation: true and handle the indentation detection. Thanks for your help! |
||
} catch { | ||
return newDefText; | ||
} | ||
}; | ||
|
||
server.onCodeAction(async ({ textDocument, range }) => { | ||
const uri = textDocument.uri; | ||
let schemaDocument = await schemas.getOpen(uri); | ||
if (!schemaDocument) { | ||
return []; | ||
} | ||
|
||
const offset = schemaDocument.textDocument.offsetAt(range.start); | ||
const node = SchemaDocument.findNodeAtOffset(schemaDocument, offset); | ||
if (!node?.isSchema) { | ||
return []; | ||
} | ||
let definitionsNode; | ||
for (const schemaNode of SchemaNode.allNodes(node.root)) { | ||
if (schemaNode.keywordUri === "https://json-schema.org/keyword/definitions") { | ||
definitionsNode = schemaNode; | ||
break; | ||
} | ||
} | ||
let highestDefNumber = 0; | ||
if (definitionsNode) { | ||
const defsContent = schemaDocument.textDocument.getText().slice( | ||
definitionsNode.offset, | ||
definitionsNode.offset + definitionsNode.textLength | ||
); | ||
const defMatches = [...defsContent.matchAll(/"def(\d+)":/g)]; | ||
defMatches.forEach((match) => | ||
highestDefNumber = Math.max(highestDefNumber, parseInt(match[1], 10)) | ||
); | ||
Comment on lines
+64
to
+71
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This isn't a good approach. Use |
||
} | ||
let newDefName = `def${highestDefNumber + 1}`; | ||
const extractedDef = schemaDocument.textDocument.getText(range); | ||
const newFormattedDef = formatNewDef(extractedDef); | ||
let defName = getKeywordName( | ||
/** @type {string} */ (node.root.dialectUri), | ||
"https://json-schema.org/keyword/definitions" | ||
); | ||
|
||
/** @type {CodeAction} */ | ||
const codeAction = { | ||
title: `Extract subschema to ${defName}`, | ||
kind: CodeActionKind.RefactorExtract, | ||
edit: { | ||
documentChanges: [ | ||
TextDocumentEdit.create({ uri: textDocument.uri, version: null }, [ | ||
{ | ||
range: range, | ||
newText: `{ "$ref": "#/${defName}/${newDefName}" }` | ||
}, | ||
definitionsNode | ||
? { | ||
range: { | ||
start: schemaDocument.textDocument.positionAt(definitionsNode.offset + 1), | ||
end: schemaDocument.textDocument.positionAt(definitionsNode.offset + 1) | ||
}, | ||
newText: `\n "${newDefName}": ${newFormattedDef},` | ||
} | ||
: { | ||
range: { | ||
start: schemaDocument.textDocument.positionAt(node.root.offset + node.root.textLength - 2), | ||
end: schemaDocument.textDocument.positionAt(node.root.offset + node.root.textLength - 2) | ||
}, | ||
newText: `,\n "${defName}": {\n "${newDefName}": ${newFormattedDef}\n }` | ||
} | ||
]) | ||
] | ||
} | ||
}; | ||
|
||
return [codeAction]; | ||
}); | ||
} | ||
} |
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 isn't what I meant when I suggested jsonc-parser. You're not using it for anything you couldn't have used
JSON.parse
for. Look for theformat
function fromjsonc-parser
. This solution usingJSON.stringify
to format isn't going to work for embedded schemas.jsoncParser.format
should help get around that problem because you can give it the whole document with the replaced text and tell it to format just the replaced text.