diff --git a/.gitignore b/.gitignore
new file mode 100644
index 00000000..8e5962ee
--- /dev/null
+++ b/.gitignore
@@ -0,0 +1,2 @@
+out
+node_modules
\ No newline at end of file
diff --git a/.vscode/launch.json b/.vscode/launch.json
new file mode 100644
index 00000000..c77b2adf
--- /dev/null
+++ b/.vscode/launch.json
@@ -0,0 +1,28 @@
+// A launch configuration that compiles the extension and then opens it inside a new window
+{
+ "version": "0.1.0",
+ "configurations": [
+ {
+ "name": "Launch Extension",
+ "type": "extensionHost",
+ "request": "launch",
+ "runtimeExecutable": "${execPath}",
+ "args": ["--extensionDevelopmentPath=${workspaceRoot}" ],
+ "stopOnEntry": false,
+ "sourceMaps": true,
+ "outDir": "${workspaceRoot}/out/src",
+ "preLaunchTask": "npm"
+ },
+ {
+ "name": "Launch Tests",
+ "type": "extensionHost",
+ "request": "launch",
+ "runtimeExecutable": "${execPath}",
+ "args": ["--extensionDevelopmentPath=${workspaceRoot}", "--extensionTestsPath=${workspaceRoot}/out/test" ],
+ "stopOnEntry": false,
+ "sourceMaps": true,
+ "outDir": "${workspaceRoot}/out/test",
+ "preLaunchTask": "npm"
+ }
+ ]
+}
diff --git a/.vscode/settings.json b/.vscode/settings.json
new file mode 100644
index 00000000..b0d48d29
--- /dev/null
+++ b/.vscode/settings.json
@@ -0,0 +1,11 @@
+// Place your settings in this file to overwrite default and user settings.
+{
+ "files.exclude": {
+ "out": false // set this to true to hide the "out" folder with the compiled JS files
+ },
+ "search.exclude": {
+ "out": true // set this to false to include "out" folder in search results
+ },
+ "typescript.tsdk": "./node_modules/typescript/lib", // we want to use the TS server from our node_modules folder to control its version
+ "files.trimTrailingWhitespace": true
+}
\ No newline at end of file
diff --git a/.vscode/tasks.json b/.vscode/tasks.json
new file mode 100644
index 00000000..fb7f662e
--- /dev/null
+++ b/.vscode/tasks.json
@@ -0,0 +1,30 @@
+// Available variables which can be used inside of strings.
+// ${workspaceRoot}: the root folder of the team
+// ${file}: the current opened file
+// ${fileBasename}: the current opened file's basename
+// ${fileDirname}: the current opened file's dirname
+// ${fileExtname}: the current opened file's extension
+// ${cwd}: the current working directory of the spawned process
+
+// A task runner that calls a custom npm script that compiles the extension.
+{
+ "version": "0.1.0",
+
+ // we want to run npm
+ "command": "npm",
+
+ // the command is a shell script
+ "isShellCommand": true,
+
+ // show the output window only if unrecognized errors occur.
+ "showOutput": "silent",
+
+ // we run the custom script "compile" as defined in package.json
+ "args": ["run", "compile", "--loglevel", "silent"],
+
+ // The tsc compiler is started in watching mode
+ "isWatching": true,
+
+ // use the standard tsc in watch mode problem matcher to find compile problems in the output.
+ "problemMatcher": "$tsc-watch"
+}
\ No newline at end of file
diff --git a/.vscodeignore b/.vscodeignore
new file mode 100644
index 00000000..93e28ff2
--- /dev/null
+++ b/.vscodeignore
@@ -0,0 +1,9 @@
+.vscode/**
+typings/**
+out/test/**
+test/**
+src/**
+**/*.map
+.gitignore
+tsconfig.json
+vsc-extension-quickstart.md
diff --git a/README.md b/README.md
new file mode 100644
index 00000000..4decea1d
--- /dev/null
+++ b/README.md
@@ -0,0 +1,10 @@
+# VSCode CMake Tools
+
+This is a simple Visual Studio Code extension that offers CMake integration. This extension
+itself *does not* provide language support. For that I recommend
+[this extension](https://marketplace.visualstudio.com/items?itemName=twxs.cmake).
+
+This extension can be installed with ``ext install cmake-tools``.
+
+Issues? Questions? Feature requests? Create an issue on
+[the github page](https://github.com/vector-of-bool/vscode-cmake-tools).
\ No newline at end of file
diff --git a/res/icon.svg b/res/icon.svg
new file mode 100644
index 00000000..4d2d9bca
--- /dev/null
+++ b/res/icon.svg
@@ -0,0 +1,264 @@
+
+
+
+
diff --git a/src/extension.ts b/src/extension.ts
new file mode 100644
index 00000000..a29d45e2
--- /dev/null
+++ b/src/extension.ts
@@ -0,0 +1,270 @@
+'use strict';
+
+import * as vscode from 'vscode';
+import * as proc from 'child_process';
+import * as fs from 'fs';
+import * as path from 'path';
+
+
+export function activate(context: vscode.ExtensionContext) {
+ // Create an output channel where the configure and build output will go
+ const channel = vscode.window.createOutputChannel('CMake/Build');
+
+ // The diagnostics collection we talk to
+ let cmake_diagnostics = vscode.languages.createDiagnosticCollection('cmake-diags');
+
+ // Get the configuration
+ function cmakeConfig(): vscode.WorkspaceConfiguration {
+ return vscode.workspace.getConfiguration('cmake');
+ }
+ // Get the value of a CMake configuration setting
+ function config(path: string, defaultValue?: T): T {
+ return cmakeConfig().get(path, defaultValue);
+ }
+
+ // Wrap a Node-style function that takes a callback in a promise, making it awaitable
+ function doAsync(fn: Function, ...args): Promise {
+ return new Promise((resolve, reject) => {
+ fn(...args, resolve);
+ });
+ }
+
+ // Returns the build directory
+ function buildDirectory(): string {
+ const build_dir = config('buildDirectory');
+ return build_dir.replace('${workspaceRoot}', vscode.workspace.rootPath);
+ }
+
+ // Test that a command exists
+ const testHaveCommand = async function (command, args: string[] = ['--version']): Promise {
+ return await new Promise((resolve, _) => {
+ const pipe = proc.spawn(command, args);
+ pipe.on('error', () => resolve(false));
+ pipe.on('exit', () => resolve(true));
+ });
+ }
+
+ // Given a list of CMake generators, returns the first on available
+ const pickGenerator = async function (candidates: string[]): Promise {
+ for (const gen of candidates) {
+ const delegate = {
+ Ninja: async function () { return await testHaveCommand('ninja'); },
+ "MinGW Makefiles": async function () {
+ return process.platform === 'win32' && await testHaveCommand('make');
+ },
+ "NMake Makefiles": async function () {
+ return process.platform === 'win32' && await testHaveCommand('nmake', ['/?']);
+ },
+ 'Unix Makefiles': async function () {
+ return process.platform !== 'win32' && await testHaveCommand('make');
+ }
+ }[gen];
+ if (delegate === undefined) {
+ vscode.window.showErrorMessage('Unknown CMake generator "' + gen + '"');
+ continue;
+ }
+ if (await delegate())
+ return gen;
+ else
+ console.log('Genereator "' + gen + '" is not supported');
+ }
+ return null;
+ }
+
+ function executeCMake(args: string[]): void {
+ console.info('Execute cmake with arguments:', args);
+ const pipe = proc.spawn('cmake', args);
+ const status = vscode.window.setStatusBarMessage;
+ status('Executing CMake...', 1000);
+ channel.appendLine('[vscode] Executing cmake command: cmake ' + args.join(' '));
+ let stderr_acc = '';
+ pipe.stdout.on('data', (data: Uint8Array) => {
+ const str = data.toString();
+ console.log('cmake [stdout]: ' + str.trim());
+ channel.append(str);
+ status('cmake: ' + str.trim(), 1000);
+ });
+ pipe.stderr.on('data', (data: Uint8Array) => {
+ const str = data.toString();
+ console.log('cmake [stderr]: ' + str.trim());
+ stderr_acc += str;
+ channel.append(str);
+ status('cmake: ' + str.trim(), 1000);
+ });
+ pipe.on('close', (retc: Number) => {
+ console.log('cmake exited with return code ' + retc);
+ channel.appendLine('[vscode] CMake exited with status ' + retc);
+ status('CMake exited with status ' + retc, 3000);
+ if (retc !== 0) {
+ vscode.window.showErrorMessage('CMake exited with non-zero return code ' + retc + '. See CMake/Build output for details');
+ }
+
+ let rest = stderr_acc;
+ const diag_re = /CMake (.*?) at (.*?):(\d+) .*?:\s+(.*?)\s*\n\n\n((.|\n)*)/;
+ const diags: Object = {};
+ while (true) {
+ if (!rest.length) break;
+ const found = diag_re.exec(rest);
+ if (!found) break;
+ const [level, filename, linestr, what, tail] = found.slice(1);
+ const filepath =
+ path.isAbsolute(filename)
+ ? filename
+ : path.join(vscode.workspace.rootPath, filename);
+
+ const line = Number.parseInt(linestr) - 1;
+ if (!(filepath in diags)) {
+ diags[filepath] = [];
+ }
+ const file_diags: vscode.Diagnostic[] = diags[filepath];
+ const diag = new vscode.Diagnostic(
+ new vscode.Range(
+ line,
+ 0,
+ line,
+ Number.POSITIVE_INFINITY
+ ),
+ what,
+ {
+ "Warning": vscode.DiagnosticSeverity.Warning,
+ "Error": vscode.DiagnosticSeverity.Error,
+ }[level]
+ );
+ diag.source = 'CMake';
+ file_diags.push(diag);
+ rest = tail;
+ }
+
+ cmake_diagnostics.clear();
+ for (const filepath in diags) {
+ cmake_diagnostics.set(vscode.Uri.file(filepath), diags[filepath]);
+ }
+ });
+ }
+
+ const configure = vscode.commands.registerCommand('cmake.configure', async function (extra_args: string[] = []) {
+ if (!(extra_args instanceof Array)) {
+ extra_args = [];
+ }
+ const source_dir = vscode.workspace.rootPath;
+ if (!source_dir) {
+ vscode.window.showErrorMessage('You do not have a source directory open');
+ return;
+ }
+ const cmake_list = path.join(source_dir, 'CMakeLists.txt');
+ const binary_dir = buildDirectory();
+
+ const cmake_cache = path.join(binary_dir, "CMakeCache.txt");
+ channel.show();
+
+ const settings_args = ['--no-warn-unused-cli'];
+ if (!(await doAsync(fs.exists, cmake_cache))) {
+ channel.appendLine("[vscode] Setting up initial CMake configuration");
+ const generator = await pickGenerator(config("preferredGenerators"));
+ if (generator) {
+ channel.appendLine('[vscode] Configuring using the "' + generator + '" CMake generator');
+ settings_args.push("-G" + generator);
+ }
+ else {
+ console.error("None of the preferred generators was selected");
+ }
+
+ settings_args.push("-DCMAKE_BUILD_TYPE=" + config("initialBuildType"));
+ }
+
+ const settings = config