pascalabcnet/TreeConverter/NetWrappers/AssemblyResolveScope.cs
Nikolay Kuznetsov 5996f4bbc2
Linux/mono ide fixes (#3330)
* Update CodeCompletionActions.cs 

Показ Code Completion Window по Ctrl-Space, если курсор находится сразу после точки (как сделано в Win версии). Текущее поведение - после точки ccw не показывать, только если курсор переместить на несколько символов вправо от точки.

* [LIN][IDE] Update TextView.cs - fix GetVisualColumn with empty content

На некоторых дистрибутивах Линукс (Убунту в частности) возникает ошибка времени выполнения IDE,
Описываю действия - открываю сохраненную на диске программу, запускаю, закрываю, копирую текст программы  и вставляю в новую вкладку, не сохраняя, запускаю и закрываю -- в логах пошли ошибки.. но такое не всегда происходит, иногда ошибок нет.. но похоже всё таки, что проявляется, когда есть вкладка с не сохраненным кодом. Причем интересно дефолтный каталог не сохраненной программы зависит от того, из какого каталога загружалась "сохраненная" программа, но не некий "по умолчанию".

В результате иследований дошли до функции GetVisualColumn() которая подсчитывает количество столбцов текста в редактируемом документе на основе анализа графического отображения, но в случае если текст отсутствует (Document.Content  == ""), на Linux/Mono происходит краш. Предлагаю избегать подсчёта столбцов при отсутствии текста, а сразу возвращать 9 (ноль)

* [LIN][IDE] AssemblyResolveScope.cs -- fix OnAssemblyResolve event 

На некоторых дистрибутивах Linux (Ubuntu 22.04 в частности). При загрузке больших исходников, например GraphABC.pas или GraphABCHelper.pas в IDE на Linux/Mono могут перестать открываться новые вкладки редактора IDE и другие элементы интерфейса IDE. Было обнаружено, что в обработчик OnAssemblyResolve в этом случае приходит значиние поля входной переменной args.RequestingAssembly равное null. Это происходит по какой то причине и не всегда. Но если обрабатывать эту ситуацию, то IDE работает стабильно в этом случае.
2025-09-29 18:59:10 +03:00

183 lines
7.5 KiB
C#

using System;
using System.Collections.Generic;
using System.IO;
using System.Linq;
using System.Reflection;
namespace PascalABCCompiler.NetHelper
{
/// <summary>
/// Will force the application domain to load passed assemblies when requested, while ignoring the assembly
/// versions. Handles <see cref="AppDomain.AssemblyResolve"/> until disposed.
/// </summary>
public class AssemblyResolveScope : IDisposable
{
private readonly AppDomain _appDomain;
private readonly Dictionary<string, Assembly> _assembliesByName = new Dictionary<string, Assembly>();
private readonly Dictionary<string, Assembly> _assembliesByPath = new Dictionary<string, Assembly>();
public ICollection<AssemblyName> CalculateBindingRedirects()
{
var redirectedAssemblies = new HashSet<string>();
foreach (var assembly in _assembliesByName.Values)
{
foreach (var referenceName in assembly.GetReferencedAssemblies())
{
var shortName = referenceName.Name;
if (_assembliesByName.TryGetValue(shortName, out var referenceAssembly)
&& referenceAssembly.GetName().Version != referenceName.Version)
{
redirectedAssemblies.Add(shortName);
}
}
}
return redirectedAssemblies.Select(a => _assembliesByName[a].GetName()).ToList();
}
public AssemblyResolveScope(AppDomain appDomain)
{
_appDomain = appDomain;
_appDomain.AssemblyResolve += OnAssemblyResolve;
}
public Assembly PreloadAssembly(string assemblyFilePath)
{
// Convert to normalized form (slashes, name case, possible relative paths):
assemblyFilePath = Path.GetFullPath(assemblyFilePath);
if (_assembliesByPath.TryGetValue(assemblyFilePath, out var assembly))
return assembly;
assembly = NetHelper.LoadAssembly(assemblyFilePath);
_assembliesByName[assembly.GetName().Name] = assembly;
_assembliesByPath[assemblyFilePath] = assembly;
return assembly;
}
public void Dispose()
{
_appDomain.AssemblyResolve -= OnAssemblyResolve;
}
static string standartAssemblyPath = Path.GetDirectoryName(System.Reflection.Assembly.GetAssembly(typeof(string)).ManifestModule.FullyQualifiedName);
private string GetStandardAssemblyPath(string name)
{
name = name.Replace("%GAC%\\", "");
string ttn = System.IO.Path.GetFileNameWithoutExtension(name);
string tn = Path.Combine(standartAssemblyPath, name);
if (File.Exists(tn))
return tn;
if (Environment.OSVersion.Platform != PlatformID.Unix && Environment.OSVersion.Platform != PlatformID.MacOSX)
{
string windir = Path.Combine(Environment.GetEnvironmentVariable("windir"), "Microsoft.NET");
tn = windir + @"\assembly\GAC_MSIL\";
tn += ttn + "\\";
System.IO.DirectoryInfo di = new System.IO.DirectoryInfo(tn);
if (!di.Exists)
{
tn = windir + @"\assembly\GAC_64\";
tn += ttn + "\\";
di = new System.IO.DirectoryInfo(tn);
if (!di.Exists)
{
tn = windir + @"\assembly\GAC_32\";
tn += ttn + "\\";
di = new System.IO.DirectoryInfo(tn);
if (!di.Exists)
{
tn = windir + @"\assembly\GAC\";
tn += ttn + "\\";
di = new System.IO.DirectoryInfo(tn);
if (!di.Exists)
{
windir = Environment.GetEnvironmentVariable("windir");
tn = windir + @"\assembly\GAC_MSIL\";
tn += ttn + "\\";
di = new System.IO.DirectoryInfo(tn);
if (!di.Exists)
{
tn = windir + @"\assembly\GAC_64\";
tn += ttn + "\\";
di = new System.IO.DirectoryInfo(tn);
if (!di.Exists)
{
tn = windir + @"\assembly\GAC_32\";
tn += ttn + "\\";
di = new System.IO.DirectoryInfo(tn);
if (!di.Exists)
{
tn = windir + @"\assembly\GAC\";
tn += ttn + "\\";
di = new System.IO.DirectoryInfo(tn);
if (!di.Exists)
{
return null;
}
}
}
}
}
}
}
}
System.IO.DirectoryInfo[] diarr = di.GetDirectories();
tn = Path.Combine((diarr[0]).FullName, name);
}
else
{
string gac_path = "/usr/lib/mono/4.0/gac";
DirectoryInfo di = new DirectoryInfo(Path.Combine(gac_path, ttn));
if (di.Exists)
{
System.IO.DirectoryInfo[] diarr = di.GetDirectories();
tn = Path.Combine((diarr[diarr.Length - 1]).FullName, name);
}
else
return null;
}
return tn;
}
public List<string> missingAssemblies = new List<string>();
private Assembly OnAssemblyResolve(object obj, ResolveEventArgs args)
{
if (args.RequestingAssembly == null) //fix Linux/Mono IDE missworking after loading a huge pas source such as GraphABC.pas or GraphABCHelper.pas
return null;
var requestedName = new AssemblyName(args.Name);
if (_assembliesByName.TryGetValue(requestedName.Name, out var assembly))
return assembly;
var dir = NetHelper.GetAssemblyDirectory(args.RequestingAssembly);
if (string.IsNullOrEmpty(dir))
return null;
string fileName = args.Name.Substring(0, args.Name.IndexOf(",")) + ".dll";
string path = Path.Combine(dir, fileName);
if (!File.Exists(path))
{
string assmPath = GetStandardAssemblyPath(fileName);
if (assmPath != null)
path = assmPath;
}
if (File.Exists(path))
{
try
{
return PreloadAssembly(path);
}
catch
{
}
}
if (!missingAssemblies.Contains(fileName))
missingAssemblies.Add(fileName);
return null;
}
}
}