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,93 @@ 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 uri the uri of 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 ( documentURI : Uri , 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+
150+ // Step 1 : open a combo to select the binding type ("standard", "xml-model")
151+ const pickedBindingTypeOption = await window . showQuickPick ( bindingTypeOptions , { placeHolder : "Binding type" } ) ;
152+ if ( ! pickedBindingTypeOption ) {
153+ return ;
154+ }
155+ const bindingType = bindingTypes . get ( pickedBindingTypeOption . label ) ;
156+
157+ // Open a dialog to select the XSD, DTD to bind.
158+ const options : OpenDialogOptions = {
159+ canSelectMany : false ,
160+ openLabel : 'Select XSD or DTD file' ,
161+ filters : {
162+ 'Grammar files' : [ 'xsd' , 'dtd' ]
155163 }
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' ]
164+ } ;
165+
166+ const fileUri = await window . showOpenDialog ( options ) ;
167+ if ( fileUri && fileUri [ 0 ] ) {
168+ // The XSD, DTD has been selected, get the proper syntax for binding this grammar file in the XML document.
169+ const identifier = TextDocumentIdentifier . create ( documentURI . toString ( ) ) ;
170+ const grammarURI = fileUri [ 0 ] ;
171+ try {
172+ const result = await commands . executeCommand ( ServerCommandConstants . ASSOCIATE_GRAMMAR_INSERT , identifier , grammarURI . toString ( ) , bindingType ) ;
173+ // Insert the proper syntax for binding
174+ const lspTextDocumentEdit = < TextDocumentEdit > result ;
175+ const workEdits = new WorkspaceEdit ( ) ;
176+ for ( const edit of lspTextDocumentEdit . edits ) {
177+ workEdits . replace ( documentURI , languageClient . protocol2CodeConverter . asRange ( edit . range ) , edit . newText ) ;
164178 }
179+ workspace . applyEdit ( workEdits ) ; // apply the edits
180+
181+ } catch ( error ) {
182+ window . showErrorMessage ( 'Error during grammar binding: ' + error . message ) ;
165183 } ;
184+ }
185+ }
166186
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- } ;
187+ /**
188+ * Register commands used for associating grammar file (XSD,DTD) to a given XML file for command menu and CodeLens
189+ *
190+ * @param context the extension context
191+ * @param languageClient the language server client
192+ */
193+ function registerAssociationCommands ( context : ExtensionContext , languageClient : LanguageClient ) {
194+ // For CodeLens
195+ context . subscriptions . push ( commands . registerCommand ( ClientCommandConstants . OPEN_BINDING_WIZARD , async ( uri : Uri ) => {
196+ grammarAssociationCommand ( uri , languageClient )
197+ } ) ) ;
198+ // For command menu
199+ context . subscriptions . push ( commands . registerCommand ( ClientCommandConstants . COMMAND_PALETTE_BINDING_WIZARD , async ( ) => {
200+ const uri = window . activeTextEditor . document . uri ;
201+ // Run check to ensure available grammar binding command should be executed, or if error is thrown
202+ const canBind = await checkCanBindGrammar ( window . activeTextEditor . document . uri ) ;
203+ if ( canBind ) {
204+ grammarAssociationCommand ( uri , languageClient )
205+ } else {
206+ window . showErrorMessage ( `The document ${ uri . toString ( ) } is already bound with a grammar` ) ;
184207 }
185208 } ) ) ;
186209
187- }
210+ }
211+
212+ /**
213+ * Change value of 'canBindGrammar' to determine if grammar/schema can be bound
214+ *
215+ * @param document the text document
216+ * @returns the `hasGrammar` check result from server
217+ */
218+ async function checkCanBindGrammar ( documentURI : Uri ) {
219+ // Retrieve the document uri and identifier
220+ const identifier = TextDocumentIdentifier . create ( documentURI . toString ( ) ) ;
221+
222+ // Set the custom condition to watch if file already has bound grammar
223+ var result = false ;
224+ try {
225+ result = await commands . executeCommand ( ServerCommandConstants . CHECK_BOUND_GRAMMAR , identifier ) ;
226+ } catch ( error ) {
227+ console . log ( `Error while checking bound grammar : ${ error } ` ) ;
228+ }
229+
230+ return result
231+ }
0 commit comments