Skip to content
This repository has been archived by the owner on Sep 24, 2018. It is now read-only.

Template creation feature and file creation changes #3

Open
wants to merge 5 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
23 changes: 12 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -17,30 +17,31 @@
"Snippets"
],
"activationEvents": [
"onCommand:extension.createClass",
"onCommand:extension.createInterface"
"onCommand:extension.createFromList",
"onCommand:extension.createTemplate",
"onCommand:extension.editTemplate"
],
"main": "./out/src/extension",
"contributes": {
"commands": [
{
"command": "extension.createClass",
"title": "New C# Class"
"command": "extension.createFromList",
"title": "New C# File"
},
{
"command": "extension.createInterface",
"title": "New C# Interface"
"command": "extension.createTemplate",
"title": "Create New C# Template"
},
{
"command": "extension.editTemplate",
"title": "Edit Existing C# Template"
}
],
"menus": {
"explorer/context": [
{
"group": "navigation@-1",
"command": "extension.createClass"
},
{
"group": "navigation@-1",
"command": "extension.createInterface"
"command": "extension.createFromList"
}
]
}
Expand Down
76 changes: 76 additions & 0 deletions src/TemplateManager.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
import {TemplateQuickPickItem} from './TemplateQuickPickItem';
import { execFile } from 'child_process';
import * as fs from 'fs';
import * as path from 'path';
import * as vscode from 'vscode';

export class TemplateManager {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Denied

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What do you have against classes? or is it that I called it 'Manager' ?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the latter 😄

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Naming is hard, what do you suggest?


templateDirectory = vscode.extensions.getExtension('jchannon.csharpextensions').extensionPath + '/templates/';

installMessage = 'Install';

createTemplate() {
vscode.window.showInputBox({ ignoreFocusOut: true, prompt: 'Please enter template name', value: "templateName.tmpl" }).then(result => {
let newTemplateFileName = path.join(this.templateDirectory, result);
this.createNewTemplateFile(newTemplateFileName);
this.openTemplate(newTemplateFileName);
});
}

editTemplate() {
this.showTemplateQuickPick()
.then(selectedTemplate => {
var templateFileName = path.join(this.templateDirectory, selectedTemplate.template + '.tmpl');
this.openTemplate(templateFileName);
});
}

openTemplate(template: string) {
execFile('code', [this.templateDirectory, template], (error: any, stdout, stderr) => {
if (error.code === 'ENOENT') {
vscode.window.showErrorMessage('Please install \'code\' command into the PATH', this.installMessage).then(value => {
if (value === this.installMessage) {
vscode.commands.executeCommand('workbench.action.installCommandLine');
}
});
}
});
}

showTemplateQuickPick(): Thenable<TemplateQuickPickItem> {
let templates = this.getTemplateQuickPickItems();
return vscode.window.showQuickPick(Promise.all<TemplateQuickPickItem>(templates) as Thenable<Iterable<TemplateQuickPickItem>>);
}

getTemplateQuickPickItems(): PromiseLike<TemplateQuickPickItem>[] {
let templateDirectory = vscode.extensions.getExtension('jchannon.csharpextensions').extensionPath + '/templates/';
return fs.readdirSync(templateDirectory).map(file => {
let templateName = path.basename(file, '.tmpl');
return vscode.workspace.openTextDocument(path.join(templateDirectory, file)).then(doc => {
let text = doc.getText();
let regex = /\$\{Description:(.*)\}/i;
let description = text.match(regex)[1];
return new TemplateQuickPickItem(templateName, description, "", templateName);
});
});
}

createNewTemplateFile(newTemplateFile: string) {
if (!fs.exists(newTemplateFile)) {
fs.writeFileSync(newTemplateFile, `\${Description: Template Description Here}
//use \${namespace} to set namespace
//use \${name} to set name
//use \${cursor} to set where the cursor will go when the file is first opened
`);
}
}

findCursorInTemlpate(text: string) {
let cursorPos = text.indexOf('${cursor}');
let preCursor = text.substr(0, cursorPos);
let lineNum = preCursor.match(/\n/gi).length;
let charNum = preCursor.substr(preCursor.lastIndexOf('\n')).length;
return new vscode.Position(lineNum, charNum);
}
}
10 changes: 10 additions & 0 deletions src/TemplateQuickPickItem.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
import * as vscode from 'vscode';

export class TemplateQuickPickItem implements vscode.QuickPickItem
{
constructor(public label: string,
public description: string,
public detail: string,
public template: string ){
}
}
80 changes: 26 additions & 54 deletions src/extension.ts
Original file line number Diff line number Diff line change
@@ -1,63 +1,46 @@
'use strict';
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
import * as vscode from 'vscode';
import * as path from 'path';
import { ensureExtension } from './helpers';
import { TemplateManager } from './TemplateManager';
import * as fs from 'fs';
import * as os from 'os';
import * as path from 'path';
import * as vscode from 'vscode';
// The module 'vscode' contains the VS Code extensibility API
// Import the module and reference it with the alias vscode in your code below
var parentfinder = require('find-parent-dir');

// this method is called when your extension is activated
// your extension is activated the very first time the command is executed
const templateManager = new TemplateManager();
export function activate(context: vscode.ExtensionContext) {

// Use the console to output diagnostic information (console.log) and errors (console.error)
// This line of code will only be executed once when your extension is activated
//console.log('Congratulations, your extension "newclassextension" is now active!');

// The command has been defined in the package.json file
// Now provide the implementation of the command with registerCommand
// The commandId parameter must match the command field in package.json
// let disposable = vscode.commands.registerCommand('extension.sayHello', (args) => {
// // The code you place here will be executed every time your command is executed
// // Display a message box to the user
// vscode.window.showInformationMessage('Hello World!');
// });

//context.subscriptions.push(disposable);
context.subscriptions.push(vscode.commands.registerCommand('extension.createClass', createClass));
context.subscriptions.push(vscode.commands.registerCommand('extension.createInterface', createInterface));
context.subscriptions.push(vscode.commands.registerCommand('extension.createFromList', createFromList));
context.subscriptions.push(vscode.commands.registerCommand('extension.createTemplate', templateManager.createTemplate));
context.subscriptions.push(vscode.commands.registerCommand('extension.editTemplate', templateManager.editTemplate));
}

function createClass(args) {
promptAndSave(args, 'class');
// this method is called when your extension is deactivated
export function deactivate() {
}

function createInterface(args) {
promptAndSave(args, 'interface');
function createFromList(args) {
templateManager.showTemplateQuickPick()
.then(selectedTemplate => {
promptAndSave(args, selectedTemplate.template);
});
}

function promptAndSave(args, templatetype: string) {
if (args == null) {
if (args === null) {
args = { _fsPath: vscode.workspace.rootPath }
}
let incomingpath: string = args._fsPath;
vscode.window.showInputBox({ ignoreFocusOut: true, prompt: 'Please enter filename', value: 'new' + templatetype + '.cs' })
.then((filename) => {

if (typeof filename === 'undefined') {
if (filename === null) {
return;
}

filename = incomingpath + path.sep + filename;

if (path.extname(filename) !== 'cs') {
if (filename.endsWith('.')) {
filename = filename + 'cs';
} else {
filename = filename + '.cs';
}
}
ensureExtension(filename, '.cs');

var originalfilename = filename;

Expand All @@ -71,8 +54,9 @@ function promptAndSave(args, templatetype: string) {
var filenamechildpath = filename.substring(filename.lastIndexOf(newroot));

var pathSepRegEx = /\//g;
if (os.platform() === "win32")
if (os.platform() === "win32") {
pathSepRegEx = /\\/g;
}

var namespace = path.dirname(filenamechildpath);
namespace = namespace.replace(pathSepRegEx, '.');
Expand All @@ -89,12 +73,13 @@ function openTemplateAndSaveNewFile(type: string, namespace: string, filename: s

let templatefileName = type + '.tmpl';

vscode.workspace.openTextDocument(vscode.extensions.getExtension('jchannon.csharpextensions').extensionPath + '/templates/' + templatefileName)
vscode.workspace.openTextDocument(path.join(templateManager.templateDirectory, templatefileName))
.then((doc: vscode.TextDocument) => {
let text = doc.getText();
text = text.replace('${namespace}', namespace);
text = text.replace('${classname}', filename);
let cursorPosition = findCursorInTemlpate(text);
text = text.replace('${name}', filename);
text = text.replace(new RegExp(/\$\{Description:(.*)\n/i), '');
let cursorPosition = templateManager.findCursorInTemlpate(text);
text = text.replace('${cursor}', '');
fs.writeFileSync(originalfilename, text);

Expand All @@ -105,17 +90,4 @@ function openTemplateAndSaveNewFile(type: string, namespace: string, filename: s
});
});
});
}

function findCursorInTemlpate(text: string) {
let cursorPos = text.indexOf('${cursor}');
let preCursor = text.substr(0, cursorPos);
let lineNum = preCursor.match(/\n/gi).length;
let charNum = preCursor.substr(preCursor.lastIndexOf('\n')).length;
return new vscode.Position(lineNum, charNum);

}

// this method is called when your extension is deactivated
export function deactivate() {
}
12 changes: 12 additions & 0 deletions src/helpers.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { extname } from 'path';

export function ensureExtension(filename: string, extension: string) {
if (extname(filename) !== extension) {
if (filename.endsWith('.')) {
filename = filename + extension.replace('.','');
} else {
filename = filename + extension;
}
}
return filename;
}
3 changes: 2 additions & 1 deletion templates/class.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
${Description:Creates a basic C# class}
namespace ${namespace}
{
public class ${classname}
public class ${name}
{
${cursor}
}
Expand Down
3 changes: 2 additions & 1 deletion templates/interface.tmpl
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
${Description:Creates a basic C# interface}
namespace ${namespace}
{
public interface ${classname}
public interface ${name}
{
${cursor}
}
Expand Down
5 changes: 5 additions & 0 deletions templates/templateName.tmpl
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
${Description: Template Description Here}
//use ${namespace} to set namespace
//use ${name} to set name
//use ${cursor} to set where the cursor will go when the file is first opened

12 changes: 12 additions & 0 deletions tslint.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
{
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

is tslint something that needs to go in package.json

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I suppose it should be bought in as a dev dependency, but for now, I was just installing it globally.

"rules": {
"no-unused-expression": true,
"no-duplicate-variable": true,
"no-duplicate-key": true,
"no-unused-variable": true,
"curly": true,
"class-name": true,
"semicolon": ["always"],
"triple-equals": true
}
}