11import * 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" ;
33import { CancellationToken , ExecuteCommandParams , ExecuteCommandRequest , ReferencesRequest , TextDocumentIdentifier , TextDocumentEdit } from "vscode-languageclient" ;
44import { LanguageClient } from 'vscode-languageclient/node' ;
55import { 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