Skip to content

Commit

Permalink
SASS: Added basic engine support.
Browse files Browse the repository at this point in the history
Initial commit for madskristensen#418.
  • Loading branch information
am11 committed Jan 5, 2014
1 parent f65b967 commit 7fa1338
Show file tree
Hide file tree
Showing 16 changed files with 292 additions and 47 deletions.
1 change: 1 addition & 0 deletions EditorExtensions/CommandConstants.cs
Original file line number Diff line number Diff line change
Expand Up @@ -91,6 +91,7 @@ enum CommandId
// Build
BuildBundles = 0x1083,
BuildLess = 0x1084,
BuildSass = 0x1085,
BuildMinify = 0x1086,
BuildCoffeeScript = 0x1087,

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -29,10 +29,11 @@ protected override Regex ErrorParsingPattern
protected override string GetArguments(string sourceFileName, string targetFileName)
{
var args = new StringBuilder();

if (WESettings.GetBoolean(WESettings.Keys.WrapCoffeeScriptClosure))
args.Append("--bare ");

if (WESettings.GetBoolean(WESettings.Keys.CoffeeScriptSourceMaps))
if (WESettings.GetBoolean(WESettings.Keys.CoffeeScriptSourceMaps) && !InUnitTests)
args.Append("--map ");

args.AppendFormat(CultureInfo.CurrentCulture, "--output \"{0}\" --compile \"{1}\"", Path.GetDirectoryName(targetFileName), sourceFileName);
Expand Down
119 changes: 119 additions & 0 deletions EditorExtensions/Compilers/SASS/SassCompiler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
using System;
using System.Collections.Generic;
using System.Globalization;
using System.IO;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;
using System.Web.Helpers;
using MadsKristensen.EditorExtensions.Helpers;
using Microsoft.CSS.Core;

namespace MadsKristensen.EditorExtensions
{
public class SassCompiler : NodeExecutorBase
{
private static readonly string _compilerPath = Path.Combine(WebEssentialsResourceDirectory, @"nodejs\node_modules\node-sass\bin\node-sass");
private static readonly Regex _endingCurlyBraces = new Regex(@"}\W*}|}", RegexOptions.Compiled);
private static readonly Regex _linesStartingWithTwoSpaces = new Regex("(\n( *))", RegexOptions.Compiled);
private static readonly Regex _errorParsingPattern = new Regex(@"^(?<message>.+) in (?<fileName>.+) on line (?<line>\d+), column (?<column>\d+):$", RegexOptions.Multiline);
private static readonly Regex _sourceMapInCss = new Regex(@"\/\*#([^*]|[\r\n]|(\*+([^*/]|[\r\n])))*\*\/", RegexOptions.Multiline);

protected override string ServiceName
{
get { return "SASS"; }
}
protected override string CompilerPath
{
get { return _compilerPath; }
}
protected override Regex ErrorParsingPattern
{
get { return _errorParsingPattern; }
}
protected override string GetArguments(string sourceFileName, string targetFileName)
{
var args = new StringBuilder("--no-color --relative-urls ");

if (WESettings.GetBoolean(WESettings.Keys.SassSourceMaps) && !InUnitTests)
{
args.Append("--source-comments=map");
}

args.AppendFormat(CultureInfo.CurrentCulture, "\"{0}\" \"{1}\"", sourceFileName, targetFileName);
return args.ToString();
}

protected override string PostProcessResult(string resultSource, string sourceFileName, string targetFileName)
{
// Inserts an empty row between each rule and replace two space indentation with 4 space indentation
resultSource = _endingCurlyBraces.Replace(_linesStartingWithTwoSpaces.Replace(resultSource.Trim(), "$1$2"), "$&\n");
resultSource = UpdateSourceMapUrls(resultSource, targetFileName);

var message = "SASS: " + Path.GetFileName(sourceFileName) + " compiled.";

// If the caller wants us to renormalize URLs to a different filename, do so.
if (targetFileName != null && resultSource.IndexOf("url(", StringComparison.OrdinalIgnoreCase) > 0)
{
try
{
resultSource = CssUrlNormalizer.NormalizeUrls(
tree: new CssParser().Parse(resultSource, true),
targetFile: targetFileName,
oldBasePath: sourceFileName
);
}
catch (Exception ex)
{
message = "SASS: An error occurred while normalizing generated paths in " + sourceFileName + "\r\n" + ex;
}
}

Logger.Log(message);

return resultSource;
}

private static string UpdateSourceMapUrls(string content, string compiledFileName)
{
if (!WESettings.GetBoolean(WESettings.Keys.SassSourceMaps) || !File.Exists(compiledFileName))
return content;

string sourceMapFilename = compiledFileName + ".map";

if (!File.Exists(sourceMapFilename))
return content;

var updatedFileContent = GetUpdatedSourceMapFileContent(compiledFileName, sourceMapFilename);

if (updatedFileContent == null)
return content;

FileHelpers.WriteFile(updatedFileContent, sourceMapFilename);
ProjectHelpers.AddFileToProject(compiledFileName, sourceMapFilename);

return UpdateSourceLinkInCssComment(content, FileHelpers.RelativePath(compiledFileName, sourceMapFilename));
}

private static string GetUpdatedSourceMapFileContent(string cssFileName, string sourceMapFilename)
{
// Read JSON map file and deserialize.
dynamic jsonSourceMap = Json.Decode(File.ReadAllText(sourceMapFilename));

if (jsonSourceMap == null)
return null;

jsonSourceMap.sources = ((IEnumerable<dynamic>)jsonSourceMap.sources).Select(s => FileHelpers.RelativePath(cssFileName, s));
jsonSourceMap.names = new List<dynamic>(jsonSourceMap.names);
jsonSourceMap.file = Path.GetFileName(cssFileName);

return Json.Encode(jsonSourceMap);
}

private static string UpdateSourceLinkInCssComment(string content, string sourceMapRelativePath)
{ // Fix sourceMappingURL comment in CSS file with network accessible path.
return _sourceMapInCss.Replace(content,
string.Format(CultureInfo.CurrentCulture, "/*# sourceMappingURL={0} */", sourceMapRelativePath));
}
}
}
35 changes: 35 additions & 0 deletions EditorExtensions/Compilers/SASS/SassProjectCompiler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
using System.Collections.Generic;

namespace MadsKristensen.EditorExtensions
{
internal class SassProjectCompiler : ProjectCompilerBase
{
protected override string ServiceName
{
get { return "SASS"; }
}

protected override string CompileToExtension
{
get { return ".css"; }
}

protected override string CompileToLocation
{
get { return WESettings.GetString(WESettings.Keys.SassCompileToLocation); }
}

protected override NodeExecutorBase Compiler
{
get { return new SassCompiler(); }
}

protected override IEnumerable<string> Extensions
{
get
{
return new string[] { ".scss" };
}
}
}
}
2 changes: 1 addition & 1 deletion EditorExtensions/EditorExtensionsPackage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -156,7 +156,7 @@ private void HandleMenuVisibility(OleMenuCommandService mcs)
OleMenuCommand menuCommand = new OleMenuCommand((s, e) => { }, commandId);
menuCommand.BeforeQueryStatus += menuCommand_BeforeQueryStatus;
mcs.AddCommand(menuCommand);

CommandID cmdTopMenu = new CommandID(CommandGuids.guidTopMenu, (int)CommandId.TopMenu);
_topMenu = new OleMenuCommand((s, e) => { }, cmdTopMenu);
mcs.AddCommand(_topMenu);
Expand Down
5 changes: 0 additions & 5 deletions EditorExtensions/Margin/CoffeeScriptMargin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -79,10 +79,5 @@ public override bool IsSaveFileEnabled
{
get { return WESettings.GetBoolean(WESettings.Keys.GenerateJsFileFromCoffeeScript); }
}

protected override bool CanWriteToDisk(string source)
{
return !string.IsNullOrWhiteSpace(source);
}
}
}
8 changes: 0 additions & 8 deletions EditorExtensions/Margin/LessMargin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -72,13 +72,5 @@ public override bool IsSaveFileEnabled
{
get { return WESettings.GetBoolean(WESettings.Keys.GenerateCssFileFromLess) && !Path.GetFileName(Document.FilePath).StartsWith("_", StringComparison.Ordinal); }
}

protected override bool CanWriteToDisk(string source)
{
//var parser = new Microsoft.CSS.Core.CssParser();
//StyleSheet stylesheet = parser.Parse(source, false);

return true;// !string.IsNullOrWhiteSpace(stylesheet.Text);
}
}
}
19 changes: 9 additions & 10 deletions EditorExtensions/Margin/MarginBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -19,12 +19,18 @@ public abstract class MarginBase : DockPanel, IWpfTextViewMargin
private bool _isDisposed = false;
private IWpfTextViewHost _viewHost;
private string _marginName;
protected string SettingsKey { get; private set; }
private bool _showMargin;
protected bool IsFirstRun { get; private set; }
private Dispatcher _dispatcher;
private ErrorListProvider _provider;

protected string SettingsKey { get; private set; }
protected bool IsFirstRun { get; private set; }
public abstract bool IsSaveFileEnabled { get; }
public abstract bool CompileEnabled { get; }
public abstract string CompileToLocation { get; }
protected ITextDocument Document { get; set; }
protected virtual bool CanWriteToDisk { get { return true; } }

protected MarginBase()
{
_dispatcher = Dispatcher.CurrentDispatcher;
Expand Down Expand Up @@ -61,11 +67,6 @@ void Document_FileActionOccurred(object sender, TextDocumentFileActionEventArgs
}
}

public abstract bool IsSaveFileEnabled { get; }
public abstract bool CompileEnabled { get; }
public abstract string CompileToLocation { get; }
protected ITextDocument Document { get; set; }

private void Initialize(string contentType, string source)
{
_viewHost = CreateTextViewHost(contentType);
Expand Down Expand Up @@ -257,7 +258,7 @@ private static string GetTargetFileName(string sourceFileName, string CompileToL
private void WriteCompiledFile(string content, string currentFileName, string fileName)
{
if (ProjectHelpers.CheckOutFileFromSourceControl(fileName)
&& CanWriteToDisk(content)
&& CanWriteToDisk
&& FileHelpers.WriteFile(content, fileName))
{
ProjectHelpers.AddFileToProject(currentFileName, fileName);
Expand Down Expand Up @@ -336,8 +337,6 @@ private void task_Navigate(object sender, EventArgs e)
}
}

protected abstract bool CanWriteToDisk(string source);

private void ThrowIfDisposed()
{
if (_isDisposed)
Expand Down
5 changes: 0 additions & 5 deletions EditorExtensions/Margin/MarkdownMargin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,11 +161,6 @@ public override bool IsSaveFileEnabled
get { return true; }
}

protected override bool CanWriteToDisk(string source)
{
return true;
}

public override bool CompileEnabled
{
get { return WESettings.GetBoolean(WESettings.Keys.MarkdownEnableCompiler); }
Expand Down
76 changes: 76 additions & 0 deletions EditorExtensions/Margin/SassMargin.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
using System;
using System.IO;
using System.Linq;
using EnvDTE;
using Microsoft.VisualStudio.Text;

namespace MadsKristensen.EditorExtensions
{
public class SassMargin : MarginBase
{
public const string MarginName = "SassMargin";

public SassMargin(string contentType, string source, bool showMargin, ITextDocument document)
: base(source, MarginName, contentType, showMargin, document)
{ }

protected override async void StartCompiler(string source)
{
if (!CompileEnabled)
return;

string sassFilePath = Document.FilePath;

string cssFilename = GetCompiledFileName(sassFilePath, ".css", CompileEnabled ? CompileToLocation : null);

if (IsFirstRun && File.Exists(cssFilename))
{
OnCompilationDone(File.ReadAllText(cssFilename), sassFilePath);
return;
}

Logger.Log("SASS: Compiling " + Path.GetFileName(sassFilePath));

var result = await new SassCompiler().Compile(sassFilePath, cssFilename);

if (result.IsSuccess)
{
OnCompilationDone(result.Result, result.FileName);
}
else
{
result.Errors.First().Message = "SASS: " + result.Errors.First().Message;

CreateTask(result.Errors.First());

base.OnCompilationDone("ERROR:" + result.Errors.First().Message, sassFilePath);
}
}

protected override void MinifyFile(string fileName, string source)
{
if (!CompileEnabled)
return;

if (WESettings.GetBoolean(WESettings.Keys.SassMinify) && !Path.GetFileName(fileName).StartsWith("_", StringComparison.Ordinal))
{
FileHelpers.MinifyFile(fileName, source, ".css");
}
}

public override bool CompileEnabled
{
get { return WESettings.GetBoolean(WESettings.Keys.SassEnableCompiler); }
}

public override string CompileToLocation
{
get { return WESettings.GetString(WESettings.Keys.SassCompileToLocation); }
}

public override bool IsSaveFileEnabled
{
get { return WESettings.GetBoolean(WESettings.Keys.GenerateCssFileFromSass) && !Path.GetFileName(Document.FilePath).StartsWith("_", StringComparison.Ordinal); }
}
}
}
10 changes: 5 additions & 5 deletions EditorExtensions/Margin/SvgMargin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,11 @@ internal class SvgMargin : MarginBase
private WebBrowser _browser;
private string _fileName;

protected override bool CanWriteToDisk
{
get { return false; }
}

public SvgMargin(string contentType, string source, bool showMargin, ITextDocument document)
: base(source, MarginName, contentType, showMargin, document)
{
Expand Down Expand Up @@ -74,11 +79,6 @@ public override bool IsSaveFileEnabled
get { return false; }
}

protected override bool CanWriteToDisk(string source)
{
return false;
}

public override bool CompileEnabled
{
get { return true; }
Expand Down
5 changes: 0 additions & 5 deletions EditorExtensions/Margin/TypeScriptMargin.cs
Original file line number Diff line number Diff line change
Expand Up @@ -90,11 +90,6 @@ public override bool IsSaveFileEnabled
get { return false; }
}

protected override bool CanWriteToDisk(string source)
{
return false;
}

public override bool CompileEnabled
{
get { return false; }
Expand Down
Loading

0 comments on commit 7fa1338

Please sign in to comment.