Skip to content

Commit 20eedb1

Browse files
committed
Add variables for xml.fileAssociations
Adds three variables that can be used in `xml.fileAssociations`: * ${workspaceFolder} * ${fileDirname} * ${fileBasenameNoExtension} These variables can be used for both the `pattern` and the `systemId`. Closes #307 Signed-off-by: David Thompson <davthomp@redhat.com>
1 parent b44ceb9 commit 20eedb1

File tree

2 files changed

+101
-13
lines changed

2 files changed

+101
-13
lines changed

docs/Validation.md

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -199,6 +199,18 @@ Please note that you can use wildcards in the pattern (ex: `foo*.xml`):
199199

200200
In this case, all XML files that start with foo and end with .xml will be associated with the XSD (foo1.xml, foo2.xml, etc)
201201

202+
You can also use the following three variables in either the `pattern` or `systemId`:
203+
204+
| Variable | Meaning |
205+
| --------------------------- | ------------------------------------------------------------------------ |
206+
| ${workspaceFolder} | The absolute path to root folder of the workspace that is currently open |
207+
| ${fileDirname} | The absolute path to the folder of the file that is currently opened |
208+
| ${fileBasenameNoExtension} | The current opened file's basename with no file extension |
209+
210+
If one of the variables for an association can't be expanded (eg. because vscode is opened in rootless mode),
211+
the association is ignored.
212+
This feature is specific to the VSCode client.
213+
202214
## Validation with DTD grammar
203215

204216
To associate your XML with a DTD grammar you can use several strategies:
@@ -322,7 +334,17 @@ Please note that you can use wildcards in the pattern (ex: `foo*.xml`):
322334

323335
In this case, all XML files that start with foo and end with .xml will be associated with the DTD (foo1.xml, foo2.xml, etc)
324336

337+
You can also use the following three variables in either the `pattern` or `systemId`:
338+
339+
| Variable | Meaning |
340+
| --------------------------- | ------------------------------------------------------------------------ |
341+
| ${workspaceFolder} | The absolute path to root folder of the workspace that is currently open |
342+
| ${fileDirname} | The absolute path to the folder of the file that is currently opened |
343+
| ${fileBasenameNoExtension} | The current opened file's basename with no file extension |
325344

345+
If one of the variables for an association can't be expanded (eg. because vscode is opened in rootless mode),
346+
the association is ignored.
347+
This feature is specific to the VSCode client.
326348

327349
# Other Validation Settings
328350

src/extension.ts

Lines changed: 79 additions & 13 deletions
Original file line numberDiff line numberDiff line change
@@ -10,17 +10,17 @@
1010
* Microsoft Corporation - Auto Closing Tags
1111
*/
1212

13-
import { prepareExecutable } from './javaServerStarter';
14-
import { LanguageClientOptions, RevealOutputChannelOn, LanguageClient, DidChangeConfigurationNotification, RequestType, TextDocumentPositionParams, ReferencesRequest, NotificationType, MessageType } from 'vscode-languageclient';
15-
import * as requirements from './requirements';
16-
import { languages, IndentAction, workspace, window, commands, ExtensionContext, TextDocument, Position, LanguageConfiguration, Uri, extensions, Command } from "vscode";
17-
import * as path from 'path';
1813
import * as os from 'os';
19-
import { activateTagClosing, AutoCloseResult } from './tagClosing';
14+
import * as path from 'path';
15+
import { Command, commands, ExtensionContext, extensions, IndentAction, LanguageConfiguration, languages, Position, TextDocument, Uri, window, workspace, WorkspaceFolder } from "vscode";
16+
import { DidChangeConfigurationNotification, LanguageClient, LanguageClientOptions, MessageType, NotificationType, ReferencesRequest, RequestType, RevealOutputChannelOn, TextDocumentPositionParams } from 'vscode-languageclient';
2017
import { Commands } from './commands';
21-
import { onConfigurationChange, subscribeJDKChangeConfiguration } from './settings';
22-
import { collectXmlJavaExtensions, onExtensionChange } from './plugin';
18+
import { prepareExecutable } from './javaServerStarter';
2319
import { markdownPreviewProvider } from "./markdownPreviewProvider";
20+
import { collectXmlJavaExtensions, onExtensionChange } from './plugin';
21+
import * as requirements from './requirements';
22+
import { getXMLConfiguration, onConfigurationChange, subscribeJDKChangeConfiguration } from './settings';
23+
import { activateTagClosing, AutoCloseResult } from './tagClosing';
2424

2525
export interface ScopeInfo {
2626
scope: "default" | "global" | "workspace" | "folder";
@@ -249,6 +249,16 @@ export function activate(context: ExtensionContext) {
249249
}));
250250
}
251251

252+
// When the current document changes, update ${fileDirname} and ${fileBasenameNoExtension}
253+
// for the file associations, (but only if these variables are referenced),
254+
// and send the updated settings to the server
255+
context.subscriptions.push(window.onDidChangeActiveTextEditor(() => {
256+
if (isFileAssocReferencesCurrentFile()) {
257+
languageClient.sendNotification(DidChangeConfigurationNotification.type, { settings: getXMLSettings(requirements.java_home) });
258+
onConfigurationChange();
259+
}
260+
}));
261+
252262
const api: XMLExtensionApi = {
253263
// add API set catalogs to internal memory
254264
addXMLCatalogs: (catalogs: string[]) => {
@@ -349,14 +359,70 @@ export function activate(context: ExtensionContext) {
349359
xml['xml']['catalogs'].push(catalog);
350360
}
351361
})
352-
externalXmlSettings.xmlFileAssociations.forEach(element => {
353-
if (!xml['xml']['fileAssociations'].some(fileAssociation => fileAssociation.systemId === element.systemId)) {
354-
xml['xml']['fileAssociations'].push(element);
355-
}
356-
});
362+
const variableSubstitutedAssociations: XMLFileAssociation[] =
363+
xml['xml']['fileAssociations'].map((association: XMLFileAssociation): XMLFileAssociation => {
364+
365+
const currentFile: TextDocument = window.activeTextEditor.document;
366+
const currentFileUri: string = currentFile && currentFile.uri.fsPath;
367+
const currentWorkspace: WorkspaceFolder = workspace.getWorkspaceFolder(currentFile && currentFile.uri);
368+
const currentWorkspaceUri: string = (currentWorkspace && currentWorkspace.uri.fsPath)
369+
|| (workspace.workspaceFolders && workspace.workspaceFolders[0].uri.fsPath);
370+
371+
if (!currentWorkspaceUri
372+
&& (association.pattern.indexOf('&{workspaceFolder}') >= 0
373+
|| association.systemId.indexOf('&{workspaceFolder}') >= 0)) {
374+
return;
375+
}
376+
377+
if (!currentFileUri
378+
&& (association.pattern.indexOf('&{fileDirname}') >= 0
379+
|| association.systemId.indexOf('&{fileDirname}') >= 0
380+
|| association.pattern.indexOf('&{fileBasenameNoExtension}') >= 0
381+
|| association.systemId.indexOf('&{fileBasenameNoExtension}') >= 0)) {
382+
return;
383+
}
384+
385+
/**
386+
* Returns the string with the values for:
387+
* * ${workspaceFolder}
388+
* * ${fileDirname}
389+
* * ${fileBasenameNoExtension}
390+
* substituted into the string
391+
*
392+
* @param val the value to substitute the variables into
393+
* @return the string with values for the variables substituted into the string
394+
*/
395+
const subVars = (val: string): string => {
396+
let newVal: string = val.replace(/\$\{workspaceFolder\}/g, currentWorkspaceUri);
397+
newVal = newVal.replace(/\$\{fileDirname\}/g, path.dirname(currentFileUri));
398+
newVal = newVal.replace(/\$\{fileBasenameNoExtension\}/g, path.basename(currentFileUri, path.extname(currentFileUri)));
399+
return newVal;
400+
}
401+
402+
return {
403+
pattern: subVars(association.pattern),
404+
systemId: subVars(association.systemId)
405+
};
406+
});
407+
xml['xml']['fileAssociations'] = [...variableSubstitutedAssociations];
357408

358409
return xml;
359410
}
411+
412+
/**
413+
* Returns true if the the XML settings contain a file association with a variable reference to the current file and false otherwise
414+
*
415+
* @return true if the the XML settings contain a file association with a variable reference to the current file and false otherwise
416+
*/
417+
function isFileAssocReferencesCurrentFile(): boolean {
418+
for (const assoc of getXMLConfiguration().get('fileAssociations') as XMLFileAssociation[]) {
419+
if (assoc.pattern.indexOf('${fileDirname}') >= 0 || assoc.pattern.indexOf('${fileBasenameNoExtension}') >= 0
420+
|| assoc.systemId.indexOf('${fileDirname}') >= 0 || assoc.systemId.indexOf('${fileBasenameNoExtension}') >= 0) {
421+
return true;
422+
}
423+
}
424+
return false;
425+
}
360426
}
361427

362428
export function deactivate(): void {

0 commit comments

Comments
 (0)