public static XamlUnresolvedFile Create(FileName fileName, ITextSource fileContent, AXmlDocument document) { XamlUnresolvedFile file = new XamlUnresolvedFile(fileName); ReadOnlyDocument textDocument = new ReadOnlyDocument(fileContent, fileName); file.errors.AddRange(document.SyntaxErrors.Select(err => new Error(ErrorType.Error, err.Description, textDocument.GetLocation(err.StartOffset)))); var visitor = new XamlDocumentVisitor(file, textDocument); visitor.VisitDocument(document); if (visitor.TypeDefinition != null) { file.topLevel = new[] { visitor.TypeDefinition } } ; else { file.topLevel = new IUnresolvedTypeDefinition[0]; } return(file); }
public override void VisitDocument(AXmlDocument document) { currentDocument = document; AXmlElement rootElement = currentDocument.Children.OfType <AXmlElement>().FirstOrDefault(); if (rootElement != null) { string className = rootElement.GetAttributeValue(XamlConst.XamlNamespace, "Class"); string modifier = rootElement.GetAttributeValue(XamlConst.XamlNamespace, "ClassModifier"); if (className != null) { TypeDefinition = new DefaultUnresolvedTypeDefinition(className) { Kind = TypeKind.Class, UnresolvedFile = file, Accessibility = Accessibility.Public, Region = new DomRegion(file.FileName, textDocument.GetLocation(rootElement.StartOffset), textDocument.GetLocation(rootElement.EndOffset)) }; TypeDefinition.Members.Add( new DefaultUnresolvedMethod(TypeDefinition, "InitializeComponent") { Accessibility = Accessibility.Public, ReturnType = KnownTypeReference.Void }); TypeDefinition.Members.Add( new DefaultUnresolvedField(TypeDefinition, "_contentLoaded") { Accessibility = Accessibility.Private, ReturnType = KnownTypeReference.Boolean }); var connectMember = new DefaultUnresolvedMethod(TypeDefinition, "Connect") { Accessibility = Accessibility.Private, ReturnType = KnownTypeReference.Void }; connectMember.Parameters.Add(new DefaultUnresolvedParameter(KnownTypeReference.Int32, "connectionId")); connectMember.Parameters.Add(new DefaultUnresolvedParameter(KnownTypeReference.Object, "target")); TypeDefinition.Members.Add(connectMember); connectMember.ExplicitInterfaceImplementations.Add( new DefaultMemberReference(SymbolKind.Method, new GetClassTypeReference(new FullTypeName(typeof(System.Windows.Markup.IComponentConnector).FullName)), "Connect")); var browsableAttribute = new DefaultUnresolvedAttribute(new GetClassTypeReference(new FullTypeName(typeof(System.ComponentModel.EditorBrowsableAttribute).FullName))); browsableAttribute.PositionalArguments.Add( new SimpleConstantValue( new GetClassTypeReference(new FullTypeName(typeof(System.ComponentModel.EditorBrowsableAttribute).FullName)), System.ComponentModel.EditorBrowsableState.Never )); connectMember.Attributes.Add(browsableAttribute); TypeDefinition.BaseTypes.Add(CreateTypeReference(rootElement.Namespace, rootElement.LocalName)); TypeDefinition.BaseTypes.Add(new GetClassTypeReference(new FullTypeName(typeof(System.Windows.Markup.IComponentConnector).FullName))); if (modifier != null) { TypeDefinition.Accessibility = ParseAccessibility(modifier); } } } base.VisitDocument(document); }
//// todo: eliminate this method later //private static void AugmentProjectModelWithAlgorithmBase(ProjectModel model) //{ // model.Children.Add(QcAlgorithmFileModel.Value); //} public static ProjectAnalysisResult RunFullProjectAnalysis(ProjectAnalysisRequest projectAnalysisRequest) { Stopwatch sw = Stopwatch.StartNew(); ProjectAnalysisResult projectAnalysisResult = new ProjectAnalysisResult(); ProjectModel projectModel = projectAnalysisRequest.ProjectModel; // todo: Ask Jared why QCAlgorithm is a partial class and why it's not included in "common" #if false // ************************************************************************************ // NOTE: In order to get this project building cleanly, with minimal dependencies // before checking into Github, I've removed all hard QuantConnect dependencies, // including the QCAlgorithm.cs embedded resource and the assembly references to // QuantConnect.Algorithm.Interface // Augment the project model with the QCAgorithm base class // projectModel.Children.Add(QcAlgorithmFileModel.Value); // ************************************************************************************ #endif // Set up the project (if not already done) if (projectModel.ProjectContent == null) { projectModel.ProjectContent = new CSharpProjectContent(); projectModel.ProjectContent = projectModel.ProjectContent.AddAssemblyReferences(QCReferences.Value); } // For each new file, we need to integrate it into the project content var fileModelsInProject = projectModel.GetFileDescendants().ToArray(); foreach (var fileModelInProject in fileModelsInProject) { IntegrateFileModel(projectModel, fileModelInProject); } // We can return now if no code completion was requested if (projectAnalysisRequest.CodeCompletionParameters == null) { projectAnalysisResult.TimeElapsed = sw.Elapsed; return(projectAnalysisResult); } // Now it's time to give attention specifically to the matter of resolving the code completion // options. This, of course, requires a deeper analysis of the specified file... var codeCompletionParams = projectAnalysisRequest.CodeCompletionParameters; // Locate the file in the project ProjectFileModel fileModel = projectModel.FindFile(codeCompletionParams.FileId); if (fileModel == null) { throw new Exception("Specified file does not exist in this project"); } // Create a TypeSystem.ICompilation that allows resolving within the project. var compilation = projectModel.ProjectContent.CreateCompilation(); #region Resolve text cursor/caret location // The text cursor position is crucial to creating a properly-scoped type resolution context // so as to get relevant code completion suggestions. int textCursorOffset = 0; TextLocation textCursorLocation = new TextLocation(1, 1); ReadOnlyDocument doc = new ReadOnlyDocument(fileModel.Content); try { // if line and column aren't set, we'll assume that the cursor offset/index is set if (codeCompletionParams.Line == 0 && codeCompletionParams.Column == 0) { textCursorOffset = codeCompletionParams.Offset; if (textCursorOffset < 0) { textCursorOffset = 0; } textCursorLocation = doc.GetLocation(textCursorOffset); } // if either line or column are invalid (i.e. <= 0), then we'll use offset 0 instead else if (codeCompletionParams.Line <= 0 || codeCompletionParams.Column <= 0) { textCursorOffset = 0; } else { textCursorLocation = new TextLocation(codeCompletionParams.Line, codeCompletionParams.Column); textCursorOffset = doc.GetOffset(textCursorLocation); codeCompletionParams.Offset = textCursorOffset; } } catch (Exception) { textCursorOffset = 0; textCursorLocation = new TextLocation(1, 1); } finally { projectAnalysisResult.Line = textCursorLocation.Line; projectAnalysisResult.Column = textCursorLocation.Column; projectAnalysisResult.Offset = textCursorOffset; } #endregion #region Create and Refine the type resolution context as much as possible based upon the cursor position var typeResolveContext = new CSharpTypeResolveContext(compilation.MainAssembly); // Constrain the resolve context by using scope typeResolveContext = typeResolveContext .WithUsingScope(fileModel.UnresolvedFile.GetUsingScope(textCursorLocation) .Resolve(compilation)); var curDef = fileModel.UnresolvedFile.GetInnermostTypeDefinition(textCursorLocation); if (curDef != null) { var resolvedDef = curDef.Resolve(typeResolveContext).GetDefinition(); typeResolveContext = typeResolveContext.WithCurrentTypeDefinition(resolvedDef); var curMember = resolvedDef.Members.FirstOrDefault(m => m.Region.Begin <= textCursorLocation && textCursorLocation < m.BodyRegion.End); if (curMember != null) { typeResolveContext = typeResolveContext.WithCurrentMember(curMember); } } #endregion // The purpose of the rest of these steps is a little fuzzy in my mind... // I'm still trying to understand them fully and if/why they're all needed. // It seems there is some redundancy here... var completionContext = new DefaultCompletionContextProvider(doc, fileModel.UnresolvedFile); #region Add Preprocessor Symbols?? completionContext.AddSymbol("TEST"); foreach (var sym in fileModel.SyntaxTree.ConditionalSymbols) { completionContext.AddSymbol(sym); } #endregion var completionDataFactory = new CodeCompletionDataFactory(new CSharpResolver(typeResolveContext)); var completionEngine = new CSharpCompletionEngine(doc, completionContext, completionDataFactory, projectModel.ProjectContent, typeResolveContext); completionEngine.EolMarker = Environment.NewLine; completionEngine.FormattingPolicy = FormattingOptionsFactory.CreateMono(); projectModel.CompletionEngine = completionEngine; // Attach contextual info to analysis result GetDocumentContext(projectAnalysisResult, textCursorOffset, doc); // Finally, generate completion data! var completionOptions = completionEngine.GetCompletionData(textCursorOffset, projectAnalysisRequest.CodeCompletionParameters.CtrlSpace).ToArray(); projectAnalysisResult.CompletionOptions = completionOptions.OrderBy(x => x.CompletionText).ToArray(); projectAnalysisResult.AutoCompleteEmptyMatch = completionEngine.AutoCompleteEmptyMatch; projectAnalysisResult.AutoSelect = completionEngine.AutoSelect; projectAnalysisResult.DefaultCompletionString = completionEngine.DefaultCompletionString; int startPos, wordLength; if (completionEngine.TryGetCompletionWord(textCursorOffset, out startPos, out wordLength)) { //Debug.WriteLine("TryGetCompletionWord :: startpos:{0} wordlength:{1}", startPos, wordLength); string completionWord = projectAnalysisResult.CompletionWord = doc.GetText(startPos, wordLength); if (!string.IsNullOrWhiteSpace(completionWord)) { var bestMatch = projectAnalysisResult.CompletionOptions .FirstOrDefault(x => x.CompletionText.CompareTo(completionWord) >= 0); projectAnalysisResult.BestMatchToCompletionWord = bestMatch; //if (bestMatch != null) //projectAnalysisResult.BestMatchToCompletionWord = bestMatch.CompletionText; } } projectAnalysisResult.TimeElapsed = sw.Elapsed; return(projectAnalysisResult); }
public static void Roundtrip(CSharpParser parser, string fileName, string code) { // 1. Parse SyntaxTree syntaxTree = parser.Parse(code, fileName); if (parser.HasErrors) { throw new InvalidOperationException("There were parse errors."); } // 2. Output StringWriter w = new StringWriter(); syntaxTree.AcceptVisitor(new CSharpOutputVisitor(w, FormattingOptionsFactory.CreateMono())); string generatedCode = w.ToString().TrimEnd(); // 3. Compare output with original (modulo whitespaces) int pos2 = 0; for (int pos1 = 0; pos1 < code.Length; pos1++) { if (!char.IsWhiteSpace(code[pos1])) { while (pos2 < generatedCode.Length && char.IsWhiteSpace(generatedCode[pos2])) { pos2++; } if (pos2 >= generatedCode.Length || code[pos1] != generatedCode[pos2]) { ReadOnlyDocument doc = new ReadOnlyDocument(code); File.WriteAllText(Path.Combine(Program.TempPath, "roundtrip-error.cs"), generatedCode); throw new InvalidOperationException("Mismatch at " + doc.GetLocation(pos1) + " of file " + fileName); } pos2++; } } if (pos2 != generatedCode.Length) { throw new InvalidOperationException("Mismatch at end of file " + fileName); } // 3b - validate that there are no lone \r if (generatedCode.Replace(w.NewLine, "\n").IndexOf('\r') >= 0) { throw new InvalidOperationException(@"Got lone \r in " + fileName); } // 4. Parse generated output SyntaxTree generatedCU; try { generatedCU = parser.Parse(generatedCode, fileName); } catch { File.WriteAllText(Path.Combine(Program.TempPath, "roundtrip-error.cs"), generatedCode, Encoding.Unicode); throw; } if (parser.HasErrors) { File.WriteAllText(Path.Combine(Program.TempPath, "roundtrip-error.cs"), generatedCode); throw new InvalidOperationException("There were parse errors in the roundtripped " + fileName); } // 5. Compare AST1 with AST2 if (!syntaxTree.IsMatch(generatedCU)) { throw new InvalidOperationException("AST match failed for " + fileName + "."); } }
public static ProjectAnalysisResult RunFullProjectAnalysis(ProjectAnalysisRequest projectAnalysisRequest) // ProjectModel projectModel, int fileId, int line, int column) { Stopwatch sw = Stopwatch.StartNew(); ProjectAnalysisResult projectAnalysisResult = new ProjectAnalysisResult(); ProjectModel projectModel = projectAnalysisRequest.ProjectModel; // Set up the project (if not already done) if (projectModel.ProjectContent == null) { projectModel.ProjectContent = new CSharpProjectContent(); projectModel.ProjectContent = projectModel.ProjectContent.AddAssemblyReferences(QCReferences.Value); } // For each new file, we need to integrate it into the project content var fileModelsInProject = projectModel.GetFileDescendants().ToArray(); foreach (var fileModelInProject in fileModelsInProject) { IntegrateFileModel(projectModel, fileModelInProject); } // We can return now if no code completion was requested if (projectAnalysisRequest.CodeCompletionParameters == null) { projectAnalysisResult.TimeElapsed = sw.Elapsed; return(projectAnalysisResult); } // Now it's time to give attention specifically to the matter of resolving the code completion // options. This, of course, requires a deeper analysis of the specified file... var codeCompletionParams = projectAnalysisRequest.CodeCompletionParameters; // Locate the file in the project ProjectFileModel fileModel = projectModel.FindFile(codeCompletionParams.FileId); if (fileModel == null) { throw new Exception("Specified file does not exist in this project"); } // Create a TypeSystem.ICompilation that allows resolving within the project. var compilation = projectModel.ProjectContent.CreateCompilation(); #region Resolve text cursor/caret location // The text cursor position is crucial to creating a properly-scoped type resolution context // so as to get relevant code completion suggestions. int textCursorOffset; TextLocation textCursorLocation; ReadOnlyDocument doc = new ReadOnlyDocument(fileModel.Content); if (codeCompletionParams.Line == 0 && codeCompletionParams.Column == 0) { textCursorOffset = codeCompletionParams.Offset; textCursorLocation = doc.GetLocation(textCursorOffset); codeCompletionParams.Line = textCursorLocation.Line; codeCompletionParams.Column = textCursorLocation.Column; } else { textCursorLocation = new TextLocation(codeCompletionParams.Line, codeCompletionParams.Column); textCursorOffset = doc.GetOffset(textCursorLocation); codeCompletionParams.Offset = textCursorOffset; } #endregion #region Create and Refine the type resolution context as much as possible based upon the cursor position var typeResolveContext = new CSharpTypeResolveContext(compilation.MainAssembly); // Constrain the resolve context by using scope typeResolveContext = typeResolveContext .WithUsingScope(fileModel.UnresolvedFile.GetUsingScope(textCursorLocation) .Resolve(compilation)); var curDef = fileModel.UnresolvedFile.GetInnermostTypeDefinition(textCursorLocation); if (curDef != null) { var resolvedDef = curDef.Resolve(typeResolveContext).GetDefinition(); typeResolveContext = typeResolveContext.WithCurrentTypeDefinition(resolvedDef); var curMember = resolvedDef.Members.FirstOrDefault(m => m.Region.Begin <= textCursorLocation && textCursorLocation < m.BodyRegion.End); if (curMember != null) { typeResolveContext = typeResolveContext.WithCurrentMember(curMember); } } #endregion // The purpose of the rest of these steps is a little fuzzy in my mind... // I'm still trying to understand them fully and if/why they're all needed. // It seems there is some redundancy here... var completionContext = new DefaultCompletionContextProvider(doc, fileModel.UnresolvedFile); #region Add Preprocessor Symbols?? completionContext.AddSymbol("TEST"); foreach (var sym in fileModel.SyntaxTree.ConditionalSymbols) { completionContext.AddSymbol(sym); } #endregion var completionDataFactory = new TestCompletionDataFactory(new CSharpResolver(typeResolveContext)); var completionEngine = new CSharpCompletionEngine(doc, completionContext, completionDataFactory, projectModel.ProjectContent, typeResolveContext); completionEngine.EolMarker = Environment.NewLine; completionEngine.FormattingPolicy = FormattingOptionsFactory.CreateMono(); projectModel.CompletionEngine = completionEngine; #region Debugging Aid // Resolve content around text cursor int numberOfCharactersAroundCursorToResolve = 20; int firstCharOffset = textCursorOffset - numberOfCharactersAroundCursorToResolve / 2; if (firstCharOffset < 0) { firstCharOffset = 0; } if (doc.TextLength < firstCharOffset + numberOfCharactersAroundCursorToResolve) { numberOfCharactersAroundCursorToResolve = doc.TextLength - firstCharOffset; } string surroundingText = doc.GetText(firstCharOffset, numberOfCharactersAroundCursorToResolve); Debug.WriteLine("Text around cursor: [{0}]", surroundingText); #endregion // Finally, generate completion data! var completionOptions = completionEngine.GetCompletionData(textCursorOffset, projectAnalysisRequest.CodeCompletionParameters.CtrlSpace).ToArray(); projectAnalysisResult.CompletionOptions = completionEngine.GetCompletionData(textCursorOffset, projectAnalysisRequest.CodeCompletionParameters.CtrlSpace).ToArray(); projectAnalysisResult.AutoCompleteEmptyMatch = completionEngine.AutoCompleteEmptyMatch; projectAnalysisResult.AutoSelect = completionEngine.AutoSelect; projectAnalysisResult.DefaultCompletionString = completionEngine.DefaultCompletionString; int startPos, wordLength; if (completionEngine.TryGetCompletionWord(textCursorOffset, out startPos, out wordLength)) { Debug.WriteLine("TryGetCompletionWord :: startpos:{0} wordlength:{1}", startPos, wordLength); projectAnalysisResult.CompletionWord = doc.GetText(startPos, wordLength); } projectAnalysisResult.TimeElapsed = sw.Elapsed; return(projectAnalysisResult); }