public async Task <CodeAnalysisResults> RunCodeAnalysisAsync(ISourceFile file, List <UnsavedFile> unsavedFiles, Func <bool> interruptRequested) { var result = new CodeAnalysisResults(); var dataAssociation = GetAssociatedData(file); var currentUnsavedFile = unsavedFiles.FirstOrDefault(f => f.FileName == file.FilePath); var currentFileConts = currentUnsavedFile?.Contents ?? File.ReadAllText(file.FilePath); var currentFileName = currentUnsavedFile?.FileName ?? file.FilePath; TypeScriptSyntaxTree syntaxTree; // Only one analyzer at a time; the JS engine is single-threaded. TODO: Workaround with multiple JS engines await analysisThreadSemaphore.WaitAsync(); try { syntaxTree = await _typeScriptContext.BuildAstAsync(currentFileName, currentFileConts); } catch (JavaScriptException) { return(new CodeAnalysisResults { Diagnostics = new TextSegmentCollection <Diagnostic> { new Diagnostic { Project = file.Project, Line = 1, Spelling = "Code analysis language service call failed.", StartOffset = 0, File = file.Name, Level = DiagnosticLevel.Error, } } }); } finally { analysisThreadSemaphore.Release(); } #if DEBUG var syntaxTreeJsonDebug = JsonConvert.SerializeObject(syntaxTree); #endif var lineCommentMatches = LineCommentPattern.Matches(currentFileConts); foreach (Match commentMatch in lineCommentMatches) { result.SyntaxHighlightingData.Add(new OffsetSyntaxHighlightingData { Start = commentMatch.Index, Length = commentMatch.Length, Type = HighlightType.Comment }); } var blockCommentMatches = BlockCommentPattern.Matches(currentFileConts); foreach (Match commentMatch in blockCommentMatches) { result.SyntaxHighlightingData.Add(new OffsetSyntaxHighlightingData { Start = commentMatch.Index, Length = commentMatch.Length, Type = HighlightType.Comment }); } // Highlight keywords var keywordMatches = KeywordPattern.Matches(currentFileConts); foreach (Match keywordMatch in keywordMatches) { result.SyntaxHighlightingData.Add(new OffsetSyntaxHighlightingData { Start = keywordMatch.Index, Length = keywordMatch.Length, Type = HighlightType.Keyword }); } // Recursively highlight and analyze from parse tree foreach (var rootStatement in syntaxTree.Statements) { HighlightNode(rootStatement, result); } // Diagnostics // Language service has diagnostics foreach (var diagnostic in syntaxTree.ParseDiagnostics) { // Convert diagnostics result.Diagnostics.Add(new Diagnostic { Project = file.Project, Line = GetLineNumber(currentFileConts, diagnostic.Start), // TODO StartOffset = diagnostic.Start, EndOffset = diagnostic.Start + diagnostic.Length, Spelling = diagnostic.MessageText, Level = diagnostic.Category == TSBridge.Ast.Diagnostics.Diagnostic.DiagnosticCategory.Error ? DiagnosticLevel.Error : DiagnosticLevel.Warning }); } result.Diagnostics.Add(new Diagnostic { Project = file.Project, Line = 1, Spelling = "Code analysis for TypeScript is experimental and unstable. Use with caution.", StartOffset = 0, File = file.Name, Level = DiagnosticLevel.Warning, }); return(result); }
public async Task <CodeAnalysisResults> RunCodeAnalysisAsync( IEnumerable <UnsavedFile> unsavedFiles, Func <bool> interruptRequested) { var errorList = IoC.Get <IErrorList>(); var result = new CodeAnalysisResults(); var diagnostics = new List <Diagnostic>(); var file = _editor.SourceFile; var dataAssociation = GetAssociatedData(file); var currentUnsavedFile = unsavedFiles.FirstOrDefault(f => f.FileName == file.FilePath); var currentFileConts = currentUnsavedFile?.Contents ?? File.ReadAllText(file.FilePath); var currentFileName = currentUnsavedFile?.FileName ?? file.FilePath; TypeScriptSyntaxTree syntaxTree; // Only one analyzer at a time; the JS engine is single-threaded. TODO: Workaround with multiple JS engines await analysisThreadSemaphore.WaitAsync(); try { syntaxTree = await _typeScriptContext.BuildAstAsync(currentFileName, currentFileConts); } catch (JavaScriptException) { diagnostics.Add(new Diagnostic( 0, 0, _editor.SourceFile.Project.Name, _editor.SourceFile.Location, 0, "Code analysis language service call failed.", "INT001", DiagnosticLevel.Error, DiagnosticCategory.Compiler)); errorList.Remove((this, _editor.SourceFile)); errorList.Create((this, _editor.SourceFile), null, DiagnosticSourceKind.Analysis, diagnostics.ToImmutableArray()); return(new CodeAnalysisResults()); } finally { analysisThreadSemaphore.Release(); } #if DEBUG var syntaxTreeJsonDebug = JsonConvert.SerializeObject(syntaxTree); #endif var lineCommentMatches = LineCommentPattern.Matches(currentFileConts); foreach (Match commentMatch in lineCommentMatches) { result.SyntaxHighlightingData.Add(new OffsetSyntaxHighlightingData { Start = commentMatch.Index, Length = commentMatch.Length, Type = HighlightType.Comment }); } var blockCommentMatches = BlockCommentPattern.Matches(currentFileConts); foreach (Match commentMatch in blockCommentMatches) { result.SyntaxHighlightingData.Add(new OffsetSyntaxHighlightingData { Start = commentMatch.Index, Length = commentMatch.Length, Type = HighlightType.Comment }); } // Highlight keywords var keywordMatches = KeywordPattern.Matches(currentFileConts); foreach (Match keywordMatch in keywordMatches) { result.SyntaxHighlightingData.Add(new OffsetSyntaxHighlightingData { Start = keywordMatch.Index, Length = keywordMatch.Length, Type = HighlightType.Keyword }); } // Recursively highlight and analyze from parse tree foreach (var rootStatement in syntaxTree.Statements) { HighlightNode(rootStatement, result); } // Diagnostics foreach (var diagnostic in syntaxTree.ParseDiagnostics) { // Convert diagnostics diagnostics.Add(new Diagnostic( diagnostic.Start, diagnostic.Length, _editor.SourceFile.Project.Name, _editor.SourceFile.Location, GetLineNumber(currentFileConts, diagnostic.Start), diagnostic.MessageText, "INT002", diagnostic.Category == TSBridge.Ast.Diagnostics.Diagnostic.DiagnosticCategory.Error ? DiagnosticLevel.Error : DiagnosticLevel.Warning, DiagnosticCategory.Compiler )); } diagnostics.Add(new Diagnostic( 0, 0, _editor.SourceFile.Project.Name, _editor.SourceFile.Location, 0, "Code analysis for TypeScript is experimental and unstable. Use with caution.", "INT003", DiagnosticLevel.Warning, DiagnosticCategory.Compiler)); errorList.Remove((this, _editor.SourceFile)); errorList.Create((this, _editor.SourceFile), null, DiagnosticSourceKind.Analysis, diagnostics.ToImmutableArray()); return(result); }