Skip to content

Commit a720559

Browse files
author
Alexander Chen
committed
registered "bind to grammer/schema" to command palette
1 parent 475372e commit a720559

File tree

3 files changed

+106
-39
lines changed

3 files changed

+106
-39
lines changed

package.json

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -524,13 +524,22 @@
524524
"command": "xml.validation.all.files",
525525
"title": "Revalidate all opened XML files",
526526
"category": "XML"
527+
},
528+
{
529+
"command": "xml.command.bind.grammar",
530+
"title": "Bind to grammar/schema file",
531+
"category": "XML"
527532
}
528533
],
529534
"menus": {
530535
"commandPalette": [
531536
{
532537
"command": "xml.validation.current.file",
533538
"when": "editorLangId == xml"
539+
},
540+
{
541+
"command": "xml.command.bind.grammar",
542+
"when": "resourceFilename =~ /xml/ && editorIsOpen"
534543
}
535544
]
536545
},

src/commands/commandConstants.ts

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,11 @@ export namespace ClientCommandConstants {
5858
*/
5959
export const OPEN_BINDING_WIZARD = 'xml.open.binding.wizard';
6060

61+
/**
62+
* VSCode client command to open the grammar/schema binding wizard from command menu.
63+
*/
64+
export const COMMAND_PALETTE_BINDING_WIZARD = 'xml.command.bind.grammar';
65+
6166
/**
6267
* Client command to execute an XML command on XML Language Server side.
6368
*/
@@ -85,4 +90,9 @@ export namespace ServerCommandConstants {
8590
* Command to associate a grammar in a XML document
8691
*/
8792
export const ASSOCIATE_GRAMMAR_INSERT = "xml.associate.grammar.insert";
93+
94+
/**
95+
* Command to check if the current XML document is bound to a grammar
96+
*/
97+
export const CHECK_BOUND_GRAMMAR = "xml.check.bound.grammar"
8898
}

src/commands/registerCommands.ts

Lines changed: 87 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import * as path from 'path';
2-
import { commands, ExtensionContext, OpenDialogOptions, Position, QuickPickItem, Uri, window, workspace, WorkspaceEdit } from "vscode";
2+
import { commands, ExtensionContext, OpenDialogOptions, Position, QuickPickItem, Uri, window, workspace, WorkspaceEdit, Disposable } from "vscode";
33
import { CancellationToken, ExecuteCommandParams, ExecuteCommandRequest, ReferencesRequest, TextDocumentIdentifier, TextDocumentEdit } from "vscode-languageclient";
44
import { LanguageClient } from 'vscode-languageclient/node';
55
import { markdownPreviewProvider } from "../markdownPreviewProvider";
@@ -25,7 +25,7 @@ export async function registerClientServerCommands(context: ExtensionContext, la
2525

2626
registerCodeLensReferencesCommands(context, languageClient);
2727
registerValidationCommands(context);
28-
registerCodeLensAssociationCommands(context, languageClient);
28+
registerAssociationCommands(context, languageClient);
2929

3030
// Register client command to execute custom XML Language Server command
3131
context.subscriptions.push(commands.registerCommand(ClientCommandConstants.EXECUTE_WORKSPACE_COMMAND, (command, ...rest) => {
@@ -139,49 +139,97 @@ for (const label of bindingTypes.keys()) {
139139
}
140140

141141
/**
142-
* Register commands used for associating grammar file (XSD,DTD) to a given XML file
142+
* The function passed to context subscriptions for grammar association
143143
*
144-
* @param context the extension context
144+
* @param uriString the string representing the XML file path
145+
* @param languageClient the language server client
145146
*/
146-
function registerCodeLensAssociationCommands(context: ExtensionContext, languageClient: LanguageClient) {
147-
context.subscriptions.push(commands.registerCommand(ClientCommandConstants.OPEN_BINDING_WIZARD, async (uriString: string) => {
148-
// A click on Bind to grammar/schema... has been processed in the XML document which is not bound to a grammar
149-
const documentURI = Uri.parse(uriString);
150-
151-
// Step 1 : open a combo to select the binding type ("standard", "xml-model")
152-
const pickedBindingTypeOption = await window.showQuickPick(bindingTypeOptions, { placeHolder: "Binding type" });
153-
if(!pickedBindingTypeOption) {
154-
return;
147+
async function grammarAssociationCommand (uriString: string, languageClient: LanguageClient) {
148+
// A click on Bind to grammar/schema... has been processed in the XML document which is not bound to a grammar
149+
const documentURI = Uri.parse(uriString);
150+
151+
// Step 1 : open a combo to select the binding type ("standard", "xml-model")
152+
const pickedBindingTypeOption = await window.showQuickPick(bindingTypeOptions, { placeHolder: "Binding type" });
153+
if(!pickedBindingTypeOption) {
154+
return;
155+
}
156+
const bindingType = bindingTypes.get(pickedBindingTypeOption.label);
157+
158+
// Open a dialog to select the XSD, DTD to bind.
159+
const options: OpenDialogOptions = {
160+
canSelectMany: false,
161+
openLabel: 'Select XSD or DTD file',
162+
filters: {
163+
'Grammar files': ['xsd', 'dtd']
155164
}
156-
const bindingType = bindingTypes.get(pickedBindingTypeOption.label);
157-
158-
// Open a dialog to select the XSD, DTD to bind.
159-
const options: OpenDialogOptions = {
160-
canSelectMany: false,
161-
openLabel: 'Select XSD or DTD file',
162-
filters: {
163-
'Grammar files': ['xsd', 'dtd']
165+
};
166+
167+
const fileUri = await window.showOpenDialog(options);
168+
if (fileUri && fileUri[0]) {
169+
// The XSD, DTD has been selected, get the proper syntax for binding this grammar file in the XML document.
170+
const identifier = TextDocumentIdentifier.create(documentURI.toString());
171+
const grammarURI = fileUri[0];
172+
try {
173+
const result = await commands.executeCommand(ServerCommandConstants.ASSOCIATE_GRAMMAR_INSERT, identifier, grammarURI.toString(), bindingType);
174+
// Insert the proper syntax for binding
175+
const lspTextDocumentEdit = <TextDocumentEdit>result;
176+
const workEdits = new WorkspaceEdit();
177+
for (const edit of lspTextDocumentEdit.edits) {
178+
workEdits.replace(documentURI, languageClient.protocol2CodeConverter.asRange(edit.range), edit.newText);
164179
}
180+
workspace.applyEdit(workEdits); // apply the edits
181+
182+
// Hide the "Bind to grammar/schema" command
183+
commands.executeCommand('setContext', 'canBindGrammar', false)
184+
185+
} catch (error) {
186+
window.showErrorMessage('Error during grammar binding: ' + error.message);
165187
};
188+
}
189+
}
166190

167-
const fileUri = await window.showOpenDialog(options);
168-
if (fileUri && fileUri[0]) {
169-
// The XSD, DTD has been selected, get the proper syntax for binding this grammar file in the XML document.
170-
const identifier = TextDocumentIdentifier.create(documentURI.toString());
171-
const grammarURI = fileUri[0];
172-
try {
173-
const result = await commands.executeCommand(ServerCommandConstants.ASSOCIATE_GRAMMAR_INSERT, identifier, grammarURI.toString(), bindingType);
174-
// Insert the proper syntax for binding
175-
const lspTextDocumentEdit = <TextDocumentEdit>result;
176-
const workEdits = new WorkspaceEdit();
177-
for (const edit of lspTextDocumentEdit.edits) {
178-
workEdits.replace(documentURI, languageClient.protocol2CodeConverter.asRange(edit.range), edit.newText);
179-
}
180-
workspace.applyEdit(workEdits); // apply the edits
181-
} catch (error) {
182-
window.showErrorMessage('Error during grammar binding: ' + error.message);
183-
};
191+
/**
192+
* Register commands used for associating grammar file (XSD,DTD) to a given XML file for command menu and CodeLens
193+
*
194+
* @param context the extension context
195+
* @param languageClient the language server client
196+
*/
197+
function registerAssociationCommands(context: ExtensionContext, languageClient: LanguageClient) {
198+
// For CodeLens
199+
context.subscriptions.push(commands.registerCommand(ClientCommandConstants.OPEN_BINDING_WIZARD, async (uriString: string) => {
200+
grammarAssociationCommand(uriString, languageClient)
201+
}));
202+
// For command menu
203+
context.subscriptions.push(commands.registerCommand(ClientCommandConstants.COMMAND_PALETTE_BINDING_WIZARD, async () => {
204+
const uriString = window.activeTextEditor.document.fileName;
205+
// Run check to ensure available grammar binding command should be executed, or if error is thrown
206+
const canBind = await checkCanBindGrammar(window.activeTextEditor.document.uri);
207+
if (canBind) {
208+
grammarAssociationCommand(uriString, languageClient)
209+
} else {
210+
window.showErrorMessage(`The document ${uriString} is already bound with a grammar`);
184211
}
185212
}));
186213

187-
}
214+
}
215+
216+
/**
217+
* Change value of 'canBindGrammar' to determine if grammar/schema can be bound
218+
*
219+
* @param document the text document
220+
* @returns the `hasGrammar` check result from server
221+
*/
222+
async function checkCanBindGrammar(documentURI: Uri) {
223+
// Retrieve the document uri and identifier
224+
const identifier = TextDocumentIdentifier.create(documentURI.toString());
225+
226+
// Set the custom condition to watch if file already has bound grammar
227+
var result = false;
228+
try {
229+
result = await commands.executeCommand(ServerCommandConstants.CHECK_BOUND_GRAMMAR, identifier);
230+
} catch(error) {
231+
console.log(`Error while checking bound grammar : ${error}`);
232+
}
233+
234+
return result
235+
}

0 commit comments

Comments
 (0)