From 67060ad9eac3d164c039ec7a91ade12eb1d9ea0f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=D0=90=D0=BB=D0=B5=D0=BA=D1=81=D0=B0=D0=BD=D0=B4=D1=80=20?= =?UTF-8?q?=D0=97=D0=B5=D0=BC=D0=BB=D1=8F=D0=BA?= <92867056+AlexanderZemlyak@users.noreply.github.com> Date: Fri, 3 Apr 2026 23:08:57 +0300 Subject: [PATCH] =?UTF-8?q?=D0=98=D1=81=D0=BF=D1=80=D0=B0=D0=B2=D0=BB?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BA=D0=BE?= =?UTF-8?q?=D0=BC=D0=BF=D0=B8=D0=BB=D1=8F=D1=86=D0=B8=D0=B8=20=D0=B2=20Int?= =?UTF-8?q?ellisense,=20=D0=B5=D1=81=D0=BB=D0=B8=20=D1=82=D1=80=D0=B0?= =?UTF-8?q?=D0=BD=D0=B7=D0=B8=D1=82=D0=B8=D0=B2=D0=BD=D1=8B=D0=B5=20=D0=B7?= =?UTF-8?q?=D0=B0=D0=B2=D0=B8=D1=81=D0=B8=D0=BC=D0=BE=D1=81=D1=82=D0=B8=20?= =?UTF-8?q?=D0=B1=D1=8B=D0=BB=D0=B8=20=D0=BF=D0=B5=D1=80=D0=B5=D0=BA=D0=BE?= =?UTF-8?q?=D0=BC=D0=BF=D0=B8=D0=BB=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD?= =?UTF-8?q?=D1=8B=20(#3414)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * Add invalidating dependent modules in code completion parser controller * Copy fields of importedModuleScope into fictive modules for SPython * Copy changes in code completion parser controller to linux version * Refactor CodeCompletionParserController * Delete cache clearing in CodeCompletionParserController when invalidating dependent modules --- CodeCompletion/DomSyntaxTreeVisitor.cs | 10 +- CodeCompletion/SymTable.cs | 49 +++++ .../CodeCompletionParserController.cs | 174 ++++++++++------- .../CodeCompletionParserController.cs | 176 +++++++++++------- 4 files changed, 277 insertions(+), 132 deletions(-) diff --git a/CodeCompletion/DomSyntaxTreeVisitor.cs b/CodeCompletion/DomSyntaxTreeVisitor.cs index 5fc637182..0c498dccc 100644 --- a/CodeCompletion/DomSyntaxTreeVisitor.cs +++ b/CodeCompletion/DomSyntaxTreeVisitor.cs @@ -3107,7 +3107,11 @@ namespace CodeCompletion if (fictiveUnit == null) { - fictiveUnit = new InterfaceUnitScope(new SymInfo(importedModuleName + "?ModuleName", SymbolKind.Namespace, ""), null); + fictiveUnit = new InterfaceUnitScope(new SymInfo(importedModuleName + "?ModuleName", SymbolKind.Namespace, ""), null); + // TODO: настроить копирование EVA + fictiveUnit.file_name = importedModuleScope.file_name; + fictiveUnit.used_units = importedModuleScope.used_units; + ((InterfaceUnitScope)fictiveUnit).impl_scope = ((InterfaceUnitScope)importedModuleScope).impl_scope; currentScope.AddUsedUnit(fictiveUnit); } @@ -3139,6 +3143,10 @@ namespace CodeCompletion if (fictiveUnit == null) { fictiveUnit = new InterfaceUnitScope(new SymInfo(importedModuleName, SymbolKind.Namespace, ""), null); + // TODO: настроить копирование EVA + fictiveUnit.file_name = importedModuleScope.file_name; + fictiveUnit.used_units = importedModuleScope.used_units; + ((InterfaceUnitScope)fictiveUnit).impl_scope = ((InterfaceUnitScope)importedModuleScope).impl_scope; currentScope.AddUsedUnit(fictiveUnit); } diff --git a/CodeCompletion/SymTable.cs b/CodeCompletion/SymTable.cs index 090b959e0..1670bca0c 100644 --- a/CodeCompletion/SymTable.cs +++ b/CodeCompletion/SymTable.cs @@ -536,6 +536,55 @@ namespace CodeCompletion used_units.Add(unit); } + /// + /// Получить массив всех транзитивно используемых модулей (ислючая пр-ва имен). + /// По умолчанию для InterfaceUnitScope ищутся и зависимости реализации тоже. + /// + public SymScope[] GetRealUsedUnitsTransitive(bool includeImplementationDependenciesForUnit = true) + { + var usedUnits = new List(); + + if (used_units != null) + { + usedUnits.AddRange(used_units.Where(unit => unit.file_name != null)); + } + + if (this is InterfaceUnitScope interfaceScope && includeImplementationDependenciesForUnit && interfaceScope.impl_scope != null) + { + foreach (var recursiveUsedUnit in interfaceScope.impl_scope.GetRealUsedUnitsTransitive(true)) + { + if (recursiveUsedUnit == this) + continue; + + if (usedUnits.FirstOrDefault(unit => unit.file_name == recursiveUsedUnit.file_name) == null) + { + usedUnits.Add(recursiveUsedUnit); + } + } + } + + if (used_units != null) + { + foreach (var usedUnit in used_units) + { + var recursiveUsedUnits = usedUnit.GetRealUsedUnitsTransitive(includeImplementationDependenciesForUnit); + + foreach (var recursiveUsedUnit in recursiveUsedUnits) + { + if (recursiveUsedUnit == this) + continue; + + if (usedUnits.FirstOrDefault(unit => unit.file_name == recursiveUsedUnit.file_name) == null) + { + usedUnits.Add(recursiveUsedUnit); + } + } + } + } + + return usedUnits.ToArray(); + } + public virtual string GetFullName() { return si.name; diff --git a/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionParserController.cs b/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionParserController.cs index bc18190da..48d1a3b2b 100644 --- a/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionParserController.cs +++ b/VisualPascalABCNET/IB/CodeCompletion/CodeCompletionParserController.cs @@ -143,94 +143,58 @@ namespace VisualPascalABC { try { - Dictionary recomp_files = new Dictionary(StringComparer.OrdinalIgnoreCase); + HashSet recomp_files = new HashSet(StringComparer.OrdinalIgnoreCase); bool is_comp = false; foreach (string FileName in filesToParse.Keys.ToArray()) // копирование ключей обязательно, иначе будет InvalidOperationException EVA { if (filesToParse[FileName]) { + // Попытка компиляции была + filesToParse[FileName] = false; + is_comp = true; - CodeCompletion.CodeCompletionController controller = new CodeCompletion.CodeCompletionController(); string text = visualEnvironmentCompiler.SourceFilesProvider(FileName, SourceFileOperation.GetText) as string; if (string.IsNullOrEmpty(text)) text = "begin end."; - CodeCompletion.DomConverter tmp = CodeCompletion.CodeCompletionController.comp_modules[FileName] as CodeCompletion.DomConverter; - long cur_mem = Environment.WorkingSet; - CodeCompletion.DomConverter dc = controller.Compile(FileName, text); - mem_delta += Environment.WorkingSet - cur_mem; - filesToParse[FileName] = false; - if (dc.is_compiled) + + if (CompileWatchedFile(FileName, text, true)) { - //CodeCompletion.CodeCompletionController.comp_modules.Remove(file_name); - if (tmp != null && tmp.visitor.entry_scope != null) - { - tmp.visitor.entry_scope.Clear(); - if (tmp.visitor.cur_scope != null) - tmp.visitor.cur_scope.Clear(); - } - CodeCompletion.CodeCompletionController.comp_modules[FileName] = dc; - recomp_files[FileName] = FileName; - filesToParse[FileName] = false; - if (ParseInformationUpdated != null) - ParseInformationUpdated(dc.visitor.entry_scope, FileName); + // успешная компиляция + recomp_files.Add(FileName); } - else if (CodeCompletion.CodeCompletionController.comp_modules[FileName] == null) - CodeCompletion.CodeCompletionController.comp_modules[FileName] = dc; } } foreach (string FileName in filesToParse.Keys.ToArray()) // копирование ключей обязательно, иначе будет InvalidOperationException EVA { CodeCompletion.DomConverter dc = CodeCompletion.CodeCompletionController.comp_modules[FileName] as CodeCompletion.DomConverter; - CodeCompletion.SymScope ss = null; + if (dc != null) { - if (dc.visitor.entry_scope != null) ss = dc.visitor.entry_scope; - else if (dc.visitor.impl_scope != null) ss = dc.visitor.impl_scope; - int j = 0; - while (j < 2) + CodeCompletion.SymScope watchedUnitScope = dc.visitor.entry_scope; + if (watchedUnitScope != null) { - if (j == 0) + var usedUnitsTransitive = watchedUnitScope.GetRealUsedUnitsTransitive(); + + for (int i = 0; i < usedUnitsTransitive.Length; i++) { - ss = dc.visitor.entry_scope; - j++; - } - else - { - ss = dc.visitor.impl_scope; - j++; - } - if (ss != null) - { - for (int i = 0; i < ss.used_units.Count; i++) + string usedUnitFileName = usedUnitsTransitive[i].file_name; + + // Если какая-то из зависимостей была перекомпилирована + if (recomp_files.Contains(usedUnitFileName)) { - string s = ss.used_units[i].file_name; - if (s != null && filesToParse.ContainsKey(s) && recomp_files.ContainsKey(s)) + // Помечаем нужные модули для будущей перекомпиляции + InvalidateDependentModules(usedUnitsTransitive, usedUnitFileName, filesToParse, FileName); + + // Перекомпилируем текущий модуль + is_comp = true; + string text = visualEnvironmentCompiler.SourceFilesProvider(FileName, SourceFileOperation.GetText) as string; + + // Здесь третий параметр false, потому что старые данные компиляции еще могут пригодиться до новой перекомпиляции EVA + if (CompileWatchedFile(FileName, text, false)) { - is_comp = true; - CodeCompletion.CodeCompletionController controller = new CodeCompletion.CodeCompletionController(); - string text = visualEnvironmentCompiler.SourceFilesProvider(FileName, SourceFileOperation.GetText) as string; - CodeCompletion.DomConverter tmp = CodeCompletion.CodeCompletionController.comp_modules[FileName] as CodeCompletion.DomConverter; - long cur_mem = Environment.WorkingSet; - dc = controller.Compile(FileName, text); - mem_delta += Environment.WorkingSet - cur_mem; - filesToParse[FileName] = false; - CodeCompletion.CodeCompletionController.comp_modules[FileName] = dc; - if (dc.is_compiled) - { - /*if (tmp != null && tmp.stv.entry_scope != null) - { - tmp.stv.entry_scope.Clear(); - if (tmp.stv.cur_scope != null) tmp.stv.cur_scope.Clear(); - }*/ - CodeCompletion.CodeCompletionController.comp_modules[FileName] = dc; - recomp_files[FileName] = FileName; - ss.used_units[i] = dc.visitor.entry_scope; - if (ParseInformationUpdated != null) - ParseInformationUpdated(dc.visitor.entry_scope, FileName); - } - else if (CodeCompletion.CodeCompletionController.comp_modules[FileName] == null) - CodeCompletion.CodeCompletionController.comp_modules[FileName] = dc; + // успешная компиляция + recomp_files.Add(FileName); } } } @@ -248,6 +212,86 @@ namespace VisualPascalABC } + /// + /// Вызов компиляции (интеллисенсом) файла с именем fileName и содержимым fileText. + /// Возвращает true, если компиляция была успешна, false в противном случае. + /// + private bool CompileWatchedFile(string fileName, string fileText, bool clearOldScope) + { + bool success = false; + + CodeCompletion.DomConverter tmp = CodeCompletion.CodeCompletionController.comp_modules[fileName] as CodeCompletion.DomConverter; + long cur_mem = Environment.WorkingSet; + CodeCompletion.CodeCompletionController controller = new CodeCompletion.CodeCompletionController(); + CodeCompletion.DomConverter dc = controller.Compile(fileName, fileText); + mem_delta += Environment.WorkingSet - cur_mem; + + if (dc.is_compiled) + { + if (clearOldScope && tmp != null) + { + tmp.visitor.entry_scope?.Clear(); + tmp.visitor.cur_scope?.Clear(); + } + CodeCompletion.CodeCompletionController.comp_modules[fileName] = dc; + + success = true; + ParseInformationUpdated?.Invoke(dc.visitor.entry_scope, fileName); + } + else if (tmp == null) + CodeCompletion.CodeCompletionController.comp_modules[fileName] = dc; + + return success; + } + + /// + /// Помечает модули из candidateModulesToCheck и watchedFiles для будущей перекомпиляции, если они завивисимы от модуля с именем recompiledDependencyName + /// + private void InvalidateDependentModules(CodeCompletion.SymScope[] candidateModulesToCheck, string recompiledDependencyName, Dictionary watchedFiles, string currentFileForRecompiling) + { + // Скоупы модулей, соответствующих watchedFiles + var compiledWatchedScopes = watchedFiles.Where(watchedFile => watchedFile.Key != currentFileForRecompiling) + .Select(watchedFile => CodeCompletion.CodeCompletionController.comp_modules[watchedFile.Key]) + .SelectMany(converter => converter is CodeCompletion.DomConverter dc ? + new CodeCompletion.SymScope[] { dc.visitor.entry_scope, dc.visitor.impl_scope } + : Enumerable.Empty()) + .Where(sc => sc != null); + + var scopesToCheck = compiledWatchedScopes.Concat(candidateModulesToCheck).Distinct(); + + foreach (var scope in scopesToCheck) + { + string scopeFileName = scope.file_name; + + if (scope is CodeCompletion.ImplementationUnitScope) + scopeFileName = ((CodeCompletion.SymScope)scope.TopScope).file_name; + + if (scopeFileName == recompiledDependencyName) + continue; + + var usedUnitsForUnit = scope.GetRealUsedUnitsTransitive(); + + // Если в зависимостях скоупа есть recompiledDependency + if (usedUnitsForUnit.FirstOrDefault(u => u.file_name == recompiledDependencyName) != null) + { + var unitOldConverter = CodeCompletion.CodeCompletionController.comp_modules[scopeFileName]; + + if (unitOldConverter != null) + { + // Помечаем для перекомпиляции + if (watchedFiles.ContainsKey(scopeFileName)) + { + watchedFiles[scopeFileName] = true; + } + else + { + CodeCompletion.CodeCompletionController.comp_modules.Remove(scopeFileName); + } + } + } + } + } + public bool IsParsing() { return th != null && th.ThreadState == System.Threading.ThreadState.Running; diff --git a/VisualPascalABCNETLinux/IB/CodeCompletion/CodeCompletionParserController.cs b/VisualPascalABCNETLinux/IB/CodeCompletion/CodeCompletionParserController.cs index d2961ebe1..5e850f3a7 100644 --- a/VisualPascalABCNETLinux/IB/CodeCompletion/CodeCompletionParserController.cs +++ b/VisualPascalABCNETLinux/IB/CodeCompletion/CodeCompletionParserController.cs @@ -148,94 +148,58 @@ namespace VisualPascalABC { try { - Dictionary recomp_files = new Dictionary(StringComparer.OrdinalIgnoreCase); + HashSet recomp_files = new HashSet(StringComparer.OrdinalIgnoreCase); bool is_comp = false; foreach (string FileName in filesToParse.Keys.ToArray()) // копирование ключей обязательно, иначе будет InvalidOperationException EVA { - + if (filesToParse[FileName]) { + // Попытка компиляции была + filesToParse[FileName] = false; + is_comp = true; - CodeCompletion.CodeCompletionController controller = new CodeCompletion.CodeCompletionController(); string text = visualEnvironmentCompiler.SourceFilesProvider(FileName, SourceFileOperation.GetText) as string; if (string.IsNullOrEmpty(text)) text = "begin end."; - CodeCompletion.DomConverter tmp = CodeCompletion.CodeCompletionController.comp_modules[FileName] as CodeCompletion.DomConverter; - long cur_mem = Environment.WorkingSet; - CodeCompletion.DomConverter dc = controller.Compile(FileName, text); - mem_delta += Environment.WorkingSet - cur_mem; - filesToParse[FileName] = false; - if (dc.is_compiled) + + if (CompileWatchedFile(FileName, text, true)) { - //CodeCompletion.CodeCompletionController.comp_modules.Remove(file_name); - if (tmp != null && tmp.visitor.entry_scope != null) - { - tmp.visitor.entry_scope.Clear(); - if (tmp.visitor.cur_scope != null) - tmp.visitor.cur_scope.Clear(); - } - CodeCompletion.CodeCompletionController.comp_modules[FileName] = dc; - recomp_files[FileName] = FileName; - filesToParse[FileName] = false; - if (ParseInformationUpdated != null) - ParseInformationUpdated(dc.visitor.entry_scope, FileName); + // успешная компиляция + recomp_files.Add(FileName); } - else if (CodeCompletion.CodeCompletionController.comp_modules[FileName] == null) - CodeCompletion.CodeCompletionController.comp_modules[FileName] = dc; } } foreach (string FileName in filesToParse.Keys.ToArray()) // копирование ключей обязательно, иначе будет InvalidOperationException EVA { CodeCompletion.DomConverter dc = CodeCompletion.CodeCompletionController.comp_modules[FileName] as CodeCompletion.DomConverter; - CodeCompletion.SymScope ss = null; + if (dc != null) { - if (dc.visitor.entry_scope != null) ss = dc.visitor.entry_scope; - else if (dc.visitor.impl_scope != null) ss = dc.visitor.impl_scope; - int j = 0; - while (j < 2) + CodeCompletion.SymScope watchedUnitScope = dc.visitor.entry_scope; + if (watchedUnitScope != null) { - if (j == 0) + var usedUnitsTransitive = watchedUnitScope.GetRealUsedUnitsTransitive(); + + for (int i = 0; i < usedUnitsTransitive.Length; i++) { - ss = dc.visitor.entry_scope; - j++; - } - else - { - ss = dc.visitor.impl_scope; - j++; - } - if (ss != null) - { - for (int i = 0; i < ss.used_units.Count; i++) + string usedUnitFileName = usedUnitsTransitive[i].file_name; + + // Если какая-то из зависимостей была перекомпилирована + if (recomp_files.Contains(usedUnitFileName)) { - string s = ss.used_units[i].file_name; - if (s != null && filesToParse.ContainsKey(s) && recomp_files.ContainsKey(s)) + // Помечаем нужные модули для будущей перекомпиляции + InvalidateDependentModules(usedUnitsTransitive, usedUnitFileName, filesToParse, FileName); + + // Перекомпилируем текущий модуль + is_comp = true; + string text = visualEnvironmentCompiler.SourceFilesProvider(FileName, SourceFileOperation.GetText) as string; + + // Здесь третий параметр false, потому что старые данные компиляции еще могут пригодиться до новой перекомпиляции EVA + if (CompileWatchedFile(FileName, text, false)) { - is_comp = true; - CodeCompletion.CodeCompletionController controller = new CodeCompletion.CodeCompletionController(); - string text = visualEnvironmentCompiler.SourceFilesProvider(FileName, SourceFileOperation.GetText) as string; - CodeCompletion.DomConverter tmp = CodeCompletion.CodeCompletionController.comp_modules[FileName] as CodeCompletion.DomConverter; - long cur_mem = Environment.WorkingSet; - dc = controller.Compile(FileName, text); - mem_delta += Environment.WorkingSet - cur_mem; - filesToParse[FileName] = false; - CodeCompletion.CodeCompletionController.comp_modules[FileName] = dc; - if (dc.is_compiled) - { - /*if (tmp != null && tmp.stv.entry_scope != null) - { - tmp.stv.entry_scope.Clear(); - if (tmp.stv.cur_scope != null) tmp.stv.cur_scope.Clear(); - }*/ - CodeCompletion.CodeCompletionController.comp_modules[FileName] = dc; - recomp_files[FileName] = FileName; - ss.used_units[i] = dc.visitor.entry_scope; - if (ParseInformationUpdated != null) - ParseInformationUpdated(dc.visitor.entry_scope, FileName); - } - else if (CodeCompletion.CodeCompletionController.comp_modules[FileName] == null) - CodeCompletion.CodeCompletionController.comp_modules[FileName] = dc; + // успешная компиляция + recomp_files.Add(FileName); } } } @@ -253,6 +217,86 @@ namespace VisualPascalABC } + /// + /// Вызов компиляции (интеллисенсом) файла с именем fileName и содержимым fileText. + /// Возвращает true, если компиляция была успешна, false в противном случае. + /// + private bool CompileWatchedFile(string fileName, string fileText, bool clearOldScope) + { + bool success = false; + + CodeCompletion.DomConverter tmp = CodeCompletion.CodeCompletionController.comp_modules[fileName] as CodeCompletion.DomConverter; + long cur_mem = Environment.WorkingSet; + CodeCompletion.CodeCompletionController controller = new CodeCompletion.CodeCompletionController(); + CodeCompletion.DomConverter dc = controller.Compile(fileName, fileText); + mem_delta += Environment.WorkingSet - cur_mem; + + if (dc.is_compiled) + { + if (clearOldScope && tmp != null) + { + tmp.visitor.entry_scope?.Clear(); + tmp.visitor.cur_scope?.Clear(); + } + CodeCompletion.CodeCompletionController.comp_modules[fileName] = dc; + + success = true; + ParseInformationUpdated?.Invoke(dc.visitor.entry_scope, fileName); + } + else if (tmp == null) + CodeCompletion.CodeCompletionController.comp_modules[fileName] = dc; + + return success; + } + + /// + /// Помечает модули из candidateModulesToCheck и watchedFiles для будущей перекомпиляции, если они завивисимы от модуля с именем recompiledDependencyName + /// + private void InvalidateDependentModules(CodeCompletion.SymScope[] candidateModulesToCheck, string recompiledDependencyName, Dictionary watchedFiles, string currentFileForRecompiling) + { + // Скоупы модулей, соответствующих watchedFiles + var compiledWatchedScopes = watchedFiles.Where(watchedFile => watchedFile.Key != currentFileForRecompiling) + .Select(watchedFile => CodeCompletion.CodeCompletionController.comp_modules[watchedFile.Key]) + .SelectMany(converter => converter is CodeCompletion.DomConverter dc ? + new CodeCompletion.SymScope[] { dc.visitor.entry_scope, dc.visitor.impl_scope } + : Enumerable.Empty()) + .Where(sc => sc != null); + + var scopesToCheck = compiledWatchedScopes.Concat(candidateModulesToCheck).Distinct(); + + foreach (var scope in scopesToCheck) + { + string scopeFileName = scope.file_name; + + if (scope is CodeCompletion.ImplementationUnitScope) + scopeFileName = ((CodeCompletion.SymScope)scope.TopScope).file_name; + + if (scopeFileName == recompiledDependencyName) + continue; + + var usedUnitsForUnit = scope.GetRealUsedUnitsTransitive(); + + // Если в зависимостях скоупа есть recompiledDependency + if (usedUnitsForUnit.FirstOrDefault(u => u.file_name == recompiledDependencyName) != null) + { + var unitOldConverter = CodeCompletion.CodeCompletionController.comp_modules[scopeFileName]; + + if (unitOldConverter != null) + { + // Помечаем для перекомпиляции + if (watchedFiles.ContainsKey(scopeFileName)) + { + watchedFiles[scopeFileName] = true; + } + else + { + CodeCompletion.CodeCompletionController.comp_modules.Remove(scopeFileName); + } + } + } + } + } + public bool IsParsing() { return th != null && th.ThreadState == System.Threading.ThreadState.Running;