diff --git a/.gitignore b/.gitignore
index 6775fcf7..3be1b022 100644
--- a/.gitignore
+++ b/.gitignore
@@ -1,3 +1,4 @@
/init.vim
/doc/tags
.*
+*.obj
diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md
index 9aa11446..e263d37f 100644
--- a/CONTRIBUTING.md
+++ b/CONTRIBUTING.md
@@ -2,6 +2,7 @@
1. [Guidelines](#guidelines)
2. [Creating Pull Requests](#pull-requests)
+3. [Creating Pull Requests](#compiling)
@@ -40,6 +41,21 @@ table and list.
# 2.2. Adding New Options
If you add new options to the plugin, make sure to document those new options in the [README.md](README.md) file, and also
-in the [help file](doc/ale.txt). Follow the format of other options in each. Global options should appear in the README
+in the [help file](doc/ale.txt). Follow the format of other options in each. Global options should appear in the README
file, and in the relevant section in the help file, and options specific to a particular linter should go in the section
for that linter.
+
+
+
+# 3. Compiling the Windows stdin wrapper
+
+To compile the stdin wrapper program for Windows, when updating the D program, you will need to compile the program with
+[LDC](https://github.com/ldc-developers/ldc) in release mode. Download and install the Community edition of Visual Studio
+from [the Visual Studio website](https://www.visualstudio.com/downloads/) first before installing LDC. LDC typically comes in
+a ZIP you can just extract somewhere.
+
+Make sure to compile with the 32-bit architecture flag, otherwise the EXE will not run on 32-bit machines.
+
+```
+ldc2 -m32 -Oz -release stdin_wrapper.d -of=stdin-wrapper.exe
+```
diff --git a/README.md b/README.md
index a3b2f672..8f2e74f9 100644
--- a/README.md
+++ b/README.md
@@ -30,11 +30,11 @@ name. That seems to be the fairest way to arrange this table.
| Bourne Shell | [-n flag](http://linux.die.net/man/1/sh), [shellcheck](https://www.shellcheck.net/) |
| C | [gcc](https://gcc.gnu.org/) |
| CoffeeScript | [coffeelint](https://www.npmjs.com/package/coffeelint) |
-| CSS | [csslint](http://csslint.net/)^ |
-| Cython (pyrex filetype) | [cython](http://cython.org/)^ |
+| CSS | [csslint](http://csslint.net/) |
+| Cython (pyrex filetype) | [cython](http://cython.org/) |
| D | [dmd](https://dlang.org/dmd-linux.html)^ |
| Fortran | [gcc](https://gcc.gnu.org/) |
-| Haskell | [ghc](https://www.haskell.org/ghc/)^ |
+| Haskell | [ghc](https://www.haskell.org/ghc/) |
| HTML | [tidy](http://www.html-tidy.org/) |
| JavaScript | [eslint](http://eslint.org/), [jscs](http://jscs.info/), [jshint](http://jshint.com/) |
| JSON | [jsonlint](http://zaa.ch/jsonlint/) |
@@ -44,9 +44,9 @@ name. That seems to be the fairest way to arrange this table.
| SASS | [sass-lint](https://www.npmjs.com/package/sass-lint) |
| SCSS | [sass-lint](https://www.npmjs.com/package/sass-lint), [scss-lint](https://github.com/brigade/scss-lint) |
| Scala | [scalac](http://scala-lang.org) |
-| TypeScript | [tslint](https://github.com/palantir/tslint)^ |
-| Vim | [vint](https://github.com/Kuniwak/vint)^ |
-| YAML | [yamllint](https://yamllint.readthedocs.io/)^ |
+| TypeScript | [tslint](https://github.com/palantir/tslint) |
+| Vim | [vint](https://github.com/Kuniwak/vint) |
+| YAML | [yamllint](https://yamllint.readthedocs.io/) |
*^ Supported only on Unix machines via a wrapper script.*
diff --git a/plugin/ale/util.vim b/plugin/ale/util.vim
index 38e1d95d..fd9bb356 100644
--- a/plugin/ale/util.vim
+++ b/plugin/ale/util.vim
@@ -13,6 +13,10 @@ function! s:FindWrapperScript()
let path = expand(parent . '/' . 'stdin-wrapper')
if filereadable(path)
+ if has('win32')
+ return path . '.exe'
+ endif
+
return path
endif
endfor
diff --git a/stdin-wrapper.exe b/stdin-wrapper.exe
new file mode 100644
index 00000000..d79f6785
Binary files /dev/null and b/stdin-wrapper.exe differ
diff --git a/stdin_wrapper.d b/stdin_wrapper.d
new file mode 100644
index 00000000..8714bc2a
--- /dev/null
+++ b/stdin_wrapper.d
@@ -0,0 +1,84 @@
+// Author: w0rp
+// Description: This file provides a D program for implementing
+// the stdin-wrapper on Windows.
+
+import std.algorithm;
+import std.array;
+import std.file;
+import std.process;
+import std.stdio;
+import std.path;
+
+@safe
+auto createTemporaryFilename(string fileExtension) {
+ import std.uuid;
+
+ string filename;
+
+ do {
+ const randomPart = randomUUID().toString.replace("-", "_");
+
+ filename = buildPath(tempDir(), "ale_" ~ randomPart ~ fileExtension);
+ } while (exists(filename));
+
+ return filename;
+}
+
+@trusted
+void readStdinToFile(ref File tempFile) {
+ stdin.byChunk(4096).copy(tempFile.lockingTextWriter());
+}
+
+// Expand program names like "csslint" to "csslint.cmd"
+// D is not able to perform this kind of expanstion in spawnProcess
+@safe
+string expandedProgramName(string name) {
+ auto extArray = environment["PATHEXT"].split(";");
+
+ foreach(pathDir; environment["PATH"].split(";")) {
+ foreach(extension; extArray) {
+ const candidate = buildPath(pathDir, name ~ extension);
+
+ if (exists(candidate)) {
+ return candidate;
+ }
+ }
+ }
+
+ // We were given a full path for a program name, so use that.
+ if (exists(name)) {
+ return name;
+ }
+
+ return "";
+}
+
+@trusted
+int runLinterProgram(string[] args) {
+ const expandedName = expandedProgramName(args[0]);
+
+ writeln(expandedName);
+
+ if (expandedName) {
+ return wait(spawnProcess([expandedName] ~ args[1..$]));
+ }
+
+ return 1;
+}
+
+@safe
+int main(string[] args) {
+ const filename = createTemporaryFilename(args[1]);
+
+ auto tempFile = File(filename, "w");
+
+ scope(exit) {
+ tempFile.close();
+ remove(filename);
+ }
+
+ readStdinToFile(tempFile);
+ tempFile.close();
+
+ return runLinterProgram(args[2..$] ~ [filename]);
+}