diff --git a/extensions/sql-database-projects/images/dark/symbol-string.svg b/extensions/sql-database-projects/images/dark/symbol-string.svg
new file mode 100644
index 0000000000..ef5f226505
--- /dev/null
+++ b/extensions/sql-database-projects/images/dark/symbol-string.svg
@@ -0,0 +1,3 @@
+
diff --git a/extensions/sql-database-projects/images/dark/symbol-variable.svg b/extensions/sql-database-projects/images/dark/symbol-variable.svg
new file mode 100644
index 0000000000..5ee50e0e81
--- /dev/null
+++ b/extensions/sql-database-projects/images/dark/symbol-variable.svg
@@ -0,0 +1,3 @@
+
diff --git a/extensions/sql-database-projects/images/light/symbol-string.svg b/extensions/sql-database-projects/images/light/symbol-string.svg
new file mode 100644
index 0000000000..2fabca5751
--- /dev/null
+++ b/extensions/sql-database-projects/images/light/symbol-string.svg
@@ -0,0 +1,3 @@
+
diff --git a/extensions/sql-database-projects/images/light/symbol-variable.svg b/extensions/sql-database-projects/images/light/symbol-variable.svg
new file mode 100644
index 0000000000..3656d9e13a
--- /dev/null
+++ b/extensions/sql-database-projects/images/light/symbol-variable.svg
@@ -0,0 +1,3 @@
+
diff --git a/extensions/sql-database-projects/src/common/constants.ts b/extensions/sql-database-projects/src/common/constants.ts
index b3c1428d98..7dfcda638d 100644
--- a/extensions/sql-database-projects/src/common/constants.ts
+++ b/extensions/sql-database-projects/src/common/constants.ts
@@ -79,6 +79,7 @@ export const refreshDataWorkspaceCommand = 'dataworkspace.refresh';
// UI Strings
export const dataSourcesNodeName = localize('dataSourcesNodeName', "Data Sources");
export const databaseReferencesNodeName = localize('databaseReferencesNodeName', "Database References");
+export const sqlcmdVariablesNodeName = localize('sqlcmdVariablesNodeName', "SQLCMD Variables");
export const sqlConnectionStringFriendly = localize('sqlConnectionStringFriendly', "SQL connection string");
export const yesString = localize('yesString', "Yes");
export const openEulaString = localize('openEulaString', "Open License Agreement");
@@ -553,6 +554,8 @@ export enum DatabaseProjectItemType {
referencesRoot = 'databaseProject.itemType.referencesRoot',
reference = 'databaseProject.itemType.reference',
dataSourceRoot = 'databaseProject.itemType.dataSourceRoot',
+ sqlcmdVariablesRoot = 'databaseProject.itemType.sqlcmdVariablesRoot',
+ sqlcmdVariable = 'databaseProject.itemType.sqlcmdVariable'
}
// AutoRest
diff --git a/extensions/sql-database-projects/src/common/iconHelper.ts b/extensions/sql-database-projects/src/common/iconHelper.ts
index 5c32bc09d0..49d19bb2a7 100644
--- a/extensions/sql-database-projects/src/common/iconHelper.ts
+++ b/extensions/sql-database-projects/src/common/iconHelper.ts
@@ -24,6 +24,9 @@ export class IconPathHelper {
public static referenceGroup: IconPath;
public static referenceDatabase: IconPath;
+ public static sqlCmdVariablesGroup: IconPath;
+ public static sqlCmdVariable: IconPath;
+
public static refresh: IconPath;
public static folder_blue: IconPath;
public static selectConnection: IconPath;
@@ -57,6 +60,9 @@ export class IconPathHelper {
IconPathHelper.referenceGroup = IconPathHelper.makeIcon('referenceGroup');
IconPathHelper.referenceDatabase = IconPathHelper.makeIcon('reference-database');
+ IconPathHelper.sqlCmdVariablesGroup = IconPathHelper.makeIcon('symbol-string');
+ IconPathHelper.sqlCmdVariable = IconPathHelper.makeIcon('symbol-variable');
+
IconPathHelper.refresh = IconPathHelper.makeIcon('refresh', true);
IconPathHelper.folder_blue = IconPathHelper.makeIcon('folder_blue', true);
IconPathHelper.selectConnection = IconPathHelper.makeIcon('selectConnection', true);
diff --git a/extensions/sql-database-projects/src/models/tree/projectTreeItem.ts b/extensions/sql-database-projects/src/models/tree/projectTreeItem.ts
index f6d9965609..29f24f0064 100644
--- a/extensions/sql-database-projects/src/models/tree/projectTreeItem.ts
+++ b/extensions/sql-database-projects/src/models/tree/projectTreeItem.ts
@@ -16,6 +16,7 @@ import { IconPathHelper } from '../../common/iconHelper';
import { FileProjectEntry } from '../projectEntry';
import { EntryType } from 'sqldbproj';
import { DBProjectConfigurationKey } from '../../tools/netcoreTool';
+import { SqlCmdVariablesTreeItem } from './sqlcmdVariableTreeItem';
/**
* TreeNode root that represents an entire project
@@ -23,6 +24,7 @@ import { DBProjectConfigurationKey } from '../../tools/netcoreTool';
export class ProjectRootTreeItem extends BaseProjectTreeItem {
dataSourceNode: DataSourcesTreeItem;
databaseReferencesNode: DatabaseReferencesTreeItem;
+ sqlCmdVariablesNode: SqlCmdVariablesTreeItem;
fileChildren: { [childName: string]: (fileTree.FolderNode | fileTree.FileNode) } = {};
project: Project;
fileSystemUri: vscode.Uri;
@@ -34,7 +36,7 @@ export class ProjectRootTreeItem extends BaseProjectTreeItem {
this.fileSystemUri = vscode.Uri.file(project.projectFilePath);
this.dataSourceNode = new DataSourcesTreeItem(this);
this.databaseReferencesNode = new DatabaseReferencesTreeItem(this);
-
+ this.sqlCmdVariablesNode = new SqlCmdVariablesTreeItem(this);
this.construct();
}
@@ -43,6 +45,7 @@ export class ProjectRootTreeItem extends BaseProjectTreeItem {
// [8/31/2020] Hiding Data source for Preview since we do not have a way to add or update those.
// output.push(this.dataSourceNode);
output.push(this.databaseReferencesNode);
+ output.push(this.sqlCmdVariablesNode);
return output.concat(Object.values(this.fileChildren).sort(fileTree.sortFileFolderNodes));
}
diff --git a/extensions/sql-database-projects/src/models/tree/sqlcmdVariableTreeItem.ts b/extensions/sql-database-projects/src/models/tree/sqlcmdVariableTreeItem.ts
new file mode 100644
index 0000000000..ddaa53937d
--- /dev/null
+++ b/extensions/sql-database-projects/src/models/tree/sqlcmdVariableTreeItem.ts
@@ -0,0 +1,73 @@
+/*---------------------------------------------------------------------------------------------
+ * Copyright (c) Microsoft Corporation. All rights reserved.
+ * Licensed under the Source EULA. See License.txt in the project root for license information.
+ *--------------------------------------------------------------------------------------------*/
+
+import * as vscode from 'vscode';
+import * as path from 'path';
+import * as constants from '../../common/constants';
+
+import { BaseProjectTreeItem } from './baseTreeItem';
+import { ProjectRootTreeItem } from './projectTreeItem';
+import { IconPathHelper } from '../../common/iconHelper';
+
+/**
+ * Folder for containing SQLCMD variable nodes in the tree
+ */
+export class SqlCmdVariablesTreeItem extends BaseProjectTreeItem {
+ private sqlcmdVariables: SqlCmdVariableTreeItem[] = [];
+
+ constructor(project: ProjectRootTreeItem) {
+ super(vscode.Uri.file(path.join(project.projectUri.fsPath, constants.sqlcmdVariablesNodeName)), project);
+
+ this.construct();
+ }
+
+ private construct() {
+ const sqlCmdVariables = (this.parent as ProjectRootTreeItem).project.sqlCmdVariables;
+
+ if (!sqlCmdVariables) {
+ return;
+ }
+
+ for (const sqlCmdVariable of Object.keys(sqlCmdVariables)) {
+ if (sqlCmdVariable) {
+ this.sqlcmdVariables.push(new SqlCmdVariableTreeItem(sqlCmdVariable, this));
+ }
+ }
+ }
+
+ public get children(): SqlCmdVariableTreeItem[] {
+ return this.sqlcmdVariables;
+ }
+
+ public get treeItem(): vscode.TreeItem {
+ const sqlCmdVariableFolderItem = new vscode.TreeItem(this.projectUri, vscode.TreeItemCollapsibleState.Collapsed);
+ sqlCmdVariableFolderItem.contextValue = constants.DatabaseProjectItemType.sqlcmdVariablesRoot;
+ sqlCmdVariableFolderItem.iconPath = IconPathHelper.sqlCmdVariablesGroup;
+
+ return sqlCmdVariableFolderItem;
+ }
+}
+
+/**
+ * Represents a SQLCMD variable in a .sqlproj
+ */
+export class SqlCmdVariableTreeItem extends BaseProjectTreeItem {
+ constructor(private sqlcmdVar: string, sqlcmdVarsTreeItem: SqlCmdVariablesTreeItem) {
+ super(vscode.Uri.file(path.join(sqlcmdVarsTreeItem.projectUri.fsPath, sqlcmdVar)), sqlcmdVarsTreeItem);
+ }
+
+ public get children(): BaseProjectTreeItem[] {
+ return [];
+ }
+
+ public get treeItem(): vscode.TreeItem {
+ const sqlcmdVariableItem = new vscode.TreeItem(this.projectUri, vscode.TreeItemCollapsibleState.None);
+ sqlcmdVariableItem.label = this.sqlcmdVar;
+ sqlcmdVariableItem.contextValue = constants.DatabaseProjectItemType.sqlcmdVariable;
+ sqlcmdVariableItem.iconPath = IconPathHelper.sqlCmdVariable;
+
+ return sqlcmdVariableItem;
+ }
+}
diff --git a/extensions/sql-database-projects/src/test/projectTree.test.ts b/extensions/sql-database-projects/src/test/projectTree.test.ts
index c393a32007..4ac4cc2d7c 100644
--- a/extensions/sql-database-projects/src/test/projectTree.test.ts
+++ b/extensions/sql-database-projects/src/test/projectTree.test.ts
@@ -70,6 +70,7 @@ describe('Project Tree tests', function (): void {
const tree = new ProjectRootTreeItem(proj);
should(tree.children.map(x => x.projectUri.path)).deepEqual([
'/TestProj/Database References',
+ '/TestProj/SQLCMD Variables',
'/TestProj/duplicateFolder',
'/TestProj/someFolder',
'/TestProj/duplicate.sql']);
@@ -82,6 +83,7 @@ describe('Project Tree tests', function (): void {
should(tree.children.map(x => x.treeItem.contextValue)).deepEqual([
DatabaseProjectItemType.referencesRoot,
+ DatabaseProjectItemType.sqlcmdVariablesRoot,
DatabaseProjectItemType.folder,
DatabaseProjectItemType.folder,
DatabaseProjectItemType.file]);
@@ -106,12 +108,13 @@ describe('Project Tree tests', function (): void {
const tree = new ProjectRootTreeItem(proj);
should(tree.children.map(x => x.projectUri.path)).deepEqual([
'/TestProj/Database References',
+ '/TestProj/SQLCMD Variables',
'/TestProj/someFolder1']);
should(tree.children.find(x => x.projectUri.path === '/TestProj/someFolder1')?.children.map(y => y.projectUri.path)).deepEqual([
- '/TestProj/someFolder1/MyNestedFolder1',
- '/TestProj/someFolder1/MyNestedFolder2',
- '/TestProj/someFolder1/MyFile2.sql']);
+ '/TestProj/someFolder1/MyNestedFolder1',
+ '/TestProj/someFolder1/MyNestedFolder2',
+ '/TestProj/someFolder1/MyFile2.sql']);
});
it('Should be able to parse and include relative paths outside project folder', function (): void {
@@ -127,6 +130,7 @@ describe('Project Tree tests', function (): void {
const tree = new ProjectRootTreeItem(proj);
should(tree.children.map(x => x.projectUri.path)).deepEqual([
'/TestProj/Database References',
+ '/TestProj/SQLCMD Variables',
'/TestProj/MyFile1.sql',
'/TestProj/MyFile2.sql']);
});