diff --git a/VSRAD.Syntax/Core/Parser/AbstractCodeParser.cs b/VSRAD.Syntax/Core/Parser/AbstractCodeParser.cs index b5693c692..abb069e75 100644 --- a/VSRAD.Syntax/Core/Parser/AbstractCodeParser.cs +++ b/VSRAD.Syntax/Core/Parser/AbstractCodeParser.cs @@ -20,12 +20,17 @@ internal abstract class AbstractCodeParser : IParser private readonly IDocumentFactory _documentFactory; private readonly AsmType _asmType; private HashSet _instructions; + private IReadOnlyList _includes; + protected DocumentManager _manager; - protected AbstractCodeParser(IDocumentFactory documentFactory, IInstructionListManager instructionListManager, AsmType asmType) + protected AbstractCodeParser(IDocumentFactory documentFactory, IInstructionListManager instructionListManager, + IReadOnlyList includes, DocumentManager manager, AsmType asmType) { _asmType = asmType; _documentFactory = documentFactory; _instructions = new HashSet(); + _includes = includes; + _manager = manager; OtherInstructions = new HashSet(); instructionListManager.InstructionsUpdated += InstructionsUpdated; @@ -55,7 +60,7 @@ private void InstructionsUpdated(IInstructionListManager sender, AsmType asmType } } - protected async Task AddExternalDefinitionsAsync(string path, TrackingToken includeStr, IBlock block, DefinitionContainer definitionContainer) + protected async Task AddExternalDefinitionsAsync(IDocument document, string path, TrackingToken includeStr, IBlock block) { try { @@ -63,24 +68,41 @@ protected async Task AddExternalDefinitionsAsync(string path, TrackingToken incl var externalFilePath = Path.Combine(Path.GetDirectoryName(path), externalFileName); var externalDocument = _documentFactory.GetOrCreateDocument(externalFilePath); + if (externalDocument == null) + { + foreach(var curPath in _includes) + { + externalFilePath = Path.Combine(curPath, externalFileName); + externalDocument = _documentFactory.GetOrCreateDocument(externalFilePath); + if (externalDocument != null) break; + } + } + if (externalDocument != null) { var externalDocumentAnalysis = externalDocument.DocumentAnalysis; var externalAnalysisResult = await externalDocumentAnalysis .GetAnalysisResultAsync(externalDocument.CurrentSnapshot) .ConfigureAwait(false); + _manager.AddChild(document, externalDocument); - foreach (var externalDefinition in externalAnalysisResult.GetGlobalDefinitions()) - definitionContainer.Add(block, externalDefinition); + //var pContainer = _manager.GetContainerForDoc(document); + //foreach (var externalDefinition in externalAnalysisResult.GetGlobalDefinitions()) + // pContainer.Add(block, externalDefinition); } } catch (Exception e) when (e is ArgumentException || e is FileNotFoundException) { /* invalid path */ } } - protected bool TryAddReference(string tokenText, TrackingToken token, IBlock block, ITextSnapshot version, DefinitionContainer definitionContainer, CancellationToken cancellation) + protected bool TryAddReference(IDocument doc, string tokenText, TrackingToken token, IBlock block, + ITextSnapshot version, CancellationToken cancellation) { cancellation.ThrowIfCancellationRequested(); - if (definitionContainer.TryGetDefinition(tokenText, out var definitionToken)) + + var node = _manager.GetNodeForDoc(doc); + var definitionToken = SearchForToken(node, tokenText); + + if (definitionToken != null) { RadAsmTokenType referenceType; switch (definitionToken.Type) @@ -116,6 +138,19 @@ protected bool TryAddReference(string tokenText, TrackingToken token, IBlock blo return false; } + private DefinitionToken SearchForToken(DocumentNode node, string tokenText) + { + DefinitionToken token; + if (node.DefinitionContainer.TryGetDefinition(tokenText, out token)) + return token; // look for token in current node + foreach (var child in node.Children) // going deeper in recursion + { + token = SearchForToken(child, tokenText); + if (token != null) break; // found token in child node + } + return token; // can still be null, in case non of children contains token + } + protected bool TryAddInstruction(string tokenText, TrackingToken token, IBlock block, ITextSnapshot version) { if (_instructions.Contains(tokenText)) diff --git a/VSRAD.Syntax/Core/Parser/Asm1Parser.cs b/VSRAD.Syntax/Core/Parser/Asm1Parser.cs index 060325be2..a773162cc 100644 --- a/VSRAD.Syntax/Core/Parser/Asm1Parser.cs +++ b/VSRAD.Syntax/Core/Parser/Asm1Parser.cs @@ -11,6 +11,7 @@ using VSRAD.Syntax.Helpers; using VSRAD.Syntax.Options.Instructions; using VSRAD.SyntaxParser; +using VSRAD.Syntax.Options; namespace VSRAD.Syntax.Core.Parser { @@ -23,12 +24,13 @@ internal sealed class Asm1Parser : AbstractCodeParser var serviceProvider = ServiceProvider.GlobalProvider; var documentFactory = serviceProvider.GetMefService(); var instructionListManager = serviceProvider.GetMefService(); + var options = serviceProvider.GetMefService(); - return new Asm1Parser(documentFactory, instructionListManager); + return new Asm1Parser(documentFactory, instructionListManager, options.IncludePaths); }); - private Asm1Parser(IDocumentFactory documentFactory, IInstructionListManager instructionListManager) - : base(documentFactory, instructionListManager, AsmType.RadAsm) { } + private Asm1Parser(IDocumentFactory documentFactory, IInstructionListManager instructionListManager, IReadOnlyList includes) + : base(documentFactory, instructionListManager, includes, null, AsmType.RadAsm) { } public override Task RunAsync(IDocument document, ITextSnapshot version, ITokenizerCollection trackingTokens, CancellationToken cancellation) @@ -192,7 +194,7 @@ private async Task ParseAsync(IDocument document, ITextSnapshot v { var tokenText = token.GetText(version); if (!TryAddInstruction(tokenText, token, currentBlock, version) && - !TryAddReference(tokenText, token, currentBlock, version, definitionContainer, cancellation)) + !TryAddReference(document, tokenText, token, currentBlock, version, cancellation)) { if (tokens.Length - i > 1 && tokens[i + 1].Type == RadAsm1Lexer.EQ) { @@ -211,7 +213,7 @@ private async Task ParseAsync(IDocument document, ITextSnapshot v { if (tokens.Length - i > 1 && tokens[i + 1].Type == RadAsm1Lexer.STRING_LITERAL) { - await AddExternalDefinitionsAsync(document.Path, tokens[i + 1], currentBlock, definitionContainer); + await AddExternalDefinitionsAsync(document, document.Path, tokens[i + 1], currentBlock); i += 1; } } @@ -236,7 +238,7 @@ private async Task ParseAsync(IDocument document, ITextSnapshot v foreach (var (text, trackingToken, block) in referenceCandidates) { - if (!TryAddReference(text, trackingToken, block, version, definitionContainer, cancellation) && OtherInstructions.Contains(text)) + if (!TryAddReference(document, text, trackingToken, block, version, cancellation) && OtherInstructions.Contains(text)) errors.Add(new ErrorToken(trackingToken, version, ErrorMessages.InvalidInstructionSetErrorMessage)); } diff --git a/VSRAD.Syntax/Core/Parser/Asm2Parser.cs b/VSRAD.Syntax/Core/Parser/Asm2Parser.cs index a1cf47596..a7152216d 100644 --- a/VSRAD.Syntax/Core/Parser/Asm2Parser.cs +++ b/VSRAD.Syntax/Core/Parser/Asm2Parser.cs @@ -11,6 +11,7 @@ using VSRAD.Syntax.Helpers; using VSRAD.Syntax.Options.Instructions; using VSRAD.SyntaxParser; +using VSRAD.Syntax.Options; namespace VSRAD.Syntax.Core.Parser { @@ -23,12 +24,14 @@ internal sealed class Asm2Parser : AbstractCodeParser var serviceProvider = ServiceProvider.GlobalProvider; var documentFactory = serviceProvider.GetMefService(); var instructionListManager = serviceProvider.GetMefService(); + var options = serviceProvider.GetMefService(); + var manager = serviceProvider.GetMefService(); - return new Asm2Parser(documentFactory, instructionListManager); + return new Asm2Parser(documentFactory, instructionListManager, options.IncludePaths, manager); }); - private Asm2Parser(IDocumentFactory documentFactory, IInstructionListManager instructionListManager) - : base(documentFactory, instructionListManager, AsmType.RadAsm2) { } + private Asm2Parser(IDocumentFactory documentFactory, IInstructionListManager instructionListManager, IReadOnlyList includes, DocumentManager manager) + : base(documentFactory, instructionListManager, includes, manager, AsmType.RadAsm2) { } public override Task RunAsync(IDocument document, ITextSnapshot version, ITokenizerCollection trackingTokens, CancellationToken cancellation) @@ -54,9 +57,9 @@ private async Task ParseAsync(IDocument document, ITextSnapshot v .WithCancellation(cancellation) .ToArray(); - var definitionContainer = new DefinitionContainer(); var referenceCandidates = new LinkedList<(string text, TrackingToken trackingToken, IBlock block)>(); + var definitionContainer = _manager.GetContainerForDoc(document); var blocks = new List(); var errors = new List(); IBlock currentBlock = new Block(version); @@ -196,7 +199,7 @@ private async Task ParseAsync(IDocument document, ITextSnapshot v else if (token.Type == RadAsm2Lexer.CLOSURE_IDENTIFIER) { var tokenText = token.GetText(version).Substring(1); // remove first '#' symbol - if (!TryAddReference(tokenText, token, currentBlock, version, definitionContainer, cancellation)) + if (!TryAddReference(document, tokenText, token, currentBlock, version, cancellation)) { referenceCandidates.AddLast((tokenText, token, currentBlock)); } @@ -205,7 +208,7 @@ private async Task ParseAsync(IDocument document, ITextSnapshot v { var tokenText = token.GetText(version); if (!TryAddInstruction(tokenText, token, currentBlock, version) && - !TryAddReference(tokenText, token, currentBlock, version, definitionContainer, cancellation)) + !TryAddReference(document, tokenText, token, currentBlock, version, cancellation)) { referenceCandidates.AddLast((tokenText, token, currentBlock)); } @@ -214,7 +217,7 @@ private async Task ParseAsync(IDocument document, ITextSnapshot v { if (tokens.Length - i > 1 && tokens[i + 1].Type == RadAsm2Lexer.STRING_LITERAL) { - await AddExternalDefinitionsAsync(document.Path, tokens[i + 1], currentBlock, definitionContainer); + await AddExternalDefinitionsAsync(document, document.Path, tokens[i + 1], currentBlock); i += 1; } } @@ -244,7 +247,7 @@ private async Task ParseAsync(IDocument document, ITextSnapshot v foreach (var (text, trackingToken, block) in referenceCandidates) { - if (!TryAddReference(text, trackingToken, block, version, definitionContainer, cancellation) && OtherInstructions.Contains(text)) + if (!TryAddReference(document, text, trackingToken, block, version, cancellation) && OtherInstructions.Contains(text)) errors.Add(new ErrorToken(trackingToken, version, ErrorMessages.InvalidInstructionSetErrorMessage)); } diff --git a/VSRAD.Syntax/Core/Parser/DefinitionContainer.cs b/VSRAD.Syntax/Core/Parser/DefinitionContainer.cs index 11275a73d..3ff368bb0 100644 --- a/VSRAD.Syntax/Core/Parser/DefinitionContainer.cs +++ b/VSRAD.Syntax/Core/Parser/DefinitionContainer.cs @@ -1,4 +1,5 @@ using System.Collections.Generic; +using System.ComponentModel.Composition; using VSRAD.Syntax.Core.Blocks; using VSRAD.Syntax.Core.Tokens; diff --git a/VSRAD.Syntax/Core/Parser/DocumentManager.cs b/VSRAD.Syntax/Core/Parser/DocumentManager.cs new file mode 100644 index 000000000..817700b6f --- /dev/null +++ b/VSRAD.Syntax/Core/Parser/DocumentManager.cs @@ -0,0 +1,54 @@ +using System.Collections.Generic; +using System.ComponentModel.Composition; +using System.Linq; +using VSRAD.Syntax.Core.Blocks; + +namespace VSRAD.Syntax.Core.Parser +{ + [Export(typeof(DocumentManager))] + public class DocumentManager + { + private List _documents; + + public DocumentManager() + { + _documents = new List(); + } + + public DocumentNode GetNodeForDoc(IDocument document) + { + var node = _documents.FirstOrDefault(d => d.Document == document); + if (node == default(DocumentNode)) + { + node = new DocumentNode(document); + _documents.Add(node); + } + return node; + } + + public DefinitionContainer GetContainerForDoc(IDocument document) + => GetNodeForDoc(document).DefinitionContainer; + + public void AddChild(IDocument parent, IDocument child) + { + var pNode = GetNodeForDoc(parent); + var cNode = GetNodeForDoc(child); + if (!pNode.Children.Contains(cNode)) + pNode.Children.Add(cNode); + } + } + + public sealed class DocumentNode + { + public readonly IDocument Document; + public DefinitionContainer DefinitionContainer; + public readonly List Children; + + public DocumentNode(IDocument doc) + { + Document = doc; + DefinitionContainer = new DefinitionContainer(); + Children = new List(); + } + } +} diff --git a/VSRAD.Syntax/Options/GeneralOptionProvider.cs b/VSRAD.Syntax/Options/GeneralOptionProvider.cs index a4ddcfb45..51c632caa 100644 --- a/VSRAD.Syntax/Options/GeneralOptionProvider.cs +++ b/VSRAD.Syntax/Options/GeneralOptionProvider.cs @@ -31,6 +31,7 @@ public OptionsProvider() Asm1FileExtensions = Constants.DefaultFileExtensionAsm1; Asm2FileExtensions = Constants.DefaultFileExtensionAsm2; InstructionsPaths = GetDefaultInstructionDirectoryPath(); + IncludePaths = new List(); Asm1InstructionSet = string.Empty; Asm2InstructionSet = string.Empty; AutocompleteInstructions = false; @@ -50,6 +51,7 @@ public OptionsProvider() public IReadOnlyList Asm1FileExtensions; public IReadOnlyList Asm2FileExtensions; public string InstructionsPaths; + public IReadOnlyList IncludePaths; public string Asm1InstructionSet; public string Asm2InstructionSet; public bool AutocompleteInstructions; diff --git a/VSRAD.Syntax/Options/GeneralOptions.cs b/VSRAD.Syntax/Options/GeneralOptions.cs index 17c94d61e..8948fb003 100644 --- a/VSRAD.Syntax/Options/GeneralOptions.cs +++ b/VSRAD.Syntax/Options/GeneralOptions.cs @@ -80,7 +80,7 @@ public string Asm2FileExtensions [Category("Instructions")] [DisplayName("Instruction folder paths")] - [Description("List of folder path separated by semicolon wit assembly instructions with .radasm file extension")] + [Description("List of folder paths separated by semicolon with assembly instructions with .radasm file extension")] [Editor(typeof(FolderPathsEditor), typeof(System.Drawing.Design.UITypeEditor))] public string InstructionsPaths { @@ -88,6 +88,16 @@ public string InstructionsPaths set => _optionsProvider.InstructionsPaths = value; } + [Category("Instructions")] + [DisplayName("Include paths")] + [Description("List of folder paths separated by semicolon that contains sources to be included")] + [Editor(typeof(FolderPathsEditor), typeof(System.Drawing.Design.UITypeEditor))] + public string IncludePaths + { + get => ConvertExtensionsTo(_optionsProvider.IncludePaths); + set => _optionsProvider.IncludePaths = ConvertExtensionsFrom(value); + } + [Category("Instructions")] [DisplayName("Asm1 selected set")] [Browsable(false)] @@ -156,6 +166,10 @@ public override async Task LoadAsync() InstructionsPaths = userSettingsStore.PropertyExists(InstructionCollectionName, nameof(InstructionsPaths)) ? userSettingsStore.GetString(InstructionCollectionName, nameof(InstructionsPaths)) : OptionsProvider.GetDefaultInstructionDirectoryPath(); + + IncludePaths = userSettingsStore.PropertyExists(InstructionCollectionName, nameof(IncludePaths)) + ? userSettingsStore.GetString(InstructionCollectionName, nameof(IncludePaths)) + : string.Empty; } public override async Task SaveAsync() @@ -174,6 +188,11 @@ public override async Task SaveAsync() { userSettingsStore.SetString(InstructionCollectionName, nameof(InstructionsPaths), InstructionsPaths); } + + if (!string.IsNullOrEmpty(IncludePaths)) + { + userSettingsStore.SetString(InstructionCollectionName, nameof(IncludePaths), IncludePaths); + } } private static IReadOnlyList ConvertExtensionsFrom(string str) => diff --git a/VSRAD.Syntax/VSRAD.Syntax.csproj b/VSRAD.Syntax/VSRAD.Syntax.csproj index 9c7936a45..b450763a4 100644 --- a/VSRAD.Syntax/VSRAD.Syntax.csproj +++ b/VSRAD.Syntax/VSRAD.Syntax.csproj @@ -92,6 +92,7 @@ +