diff --git a/.nuget/NuGet.exe b/.nuget/NuGet.exe index 324daa842..9f8781de0 100644 Binary files a/.nuget/NuGet.exe and b/.nuget/NuGet.exe differ diff --git a/Nitra.TestsLauncher/Nitra.TestsLauncher.nproj b/Nitra.TestsLauncher/Nitra.TestsLauncher.nproj index 28e03fbbd..db206e5ab 100644 --- a/Nitra.TestsLauncher/Nitra.TestsLauncher.nproj +++ b/Nitra.TestsLauncher/Nitra.TestsLauncher.nproj @@ -70,9 +70,6 @@ - - Code - Code diff --git a/Nitra.TestsLauncher/Utils.n b/Nitra.TestsLauncher/Utils.n index 1211df012..9f8c6f3d5 100644 --- a/Nitra.TestsLauncher/Utils.n +++ b/Nitra.TestsLauncher/Utils.n @@ -15,7 +15,6 @@ using System.Runtime.InteropServices; using System.Text; using System.Text.RegularExpressions; using System.Xml.Linq; -using Nitra.Visualizer.Serialization; using Messages = Nitra.ClientServer.Messages; namespace Nitra.Visualizer @@ -93,7 +92,7 @@ namespace Nitra.Visualizer public MakeXml(config : Messages.Config) : string { - SerializationHelper.Serialize(config) + Messages.SerializationHelper.Serialize(config) } public Escape(str: string): string @@ -109,4 +108,4 @@ namespace Nitra.Visualizer " } } -} \ No newline at end of file +} diff --git a/Nitra.TestsLauncher/ViewModels/SuiteVm.n b/Nitra.TestsLauncher/ViewModels/SuiteVm.n index c5496ace8..4efde0ef4 100644 --- a/Nitra.TestsLauncher/ViewModels/SuiteVm.n +++ b/Nitra.TestsLauncher/ViewModels/SuiteVm.n @@ -12,7 +12,6 @@ using System.Collections.ObjectModel; using System.Diagnostics; using System.IO; using System.Xml.Linq; -using Nitra.Visualizer.Serialization; using System.Reflection; using File = System.IO.File; using Messages = Nitra.ClientServer.Messages; @@ -52,7 +51,7 @@ namespace Nitra.ViewModels try { def config = if (File.Exists(_configPath)) - SerializationHelper.Deserialize(_configPath); + Messages.SerializationHelper.Deserialize(_configPath); else Messages.Config.InitNew(); Config = config; @@ -127,7 +126,7 @@ namespace Nitra.ViewModels public Save() : void { - File.WriteAllText(_configPath, SerializationHelper.Serialize(Config)); + File.WriteAllText(_configPath, Messages.SerializationHelper.Serialize(Config)); } public GetConfigWithFullPaths() : Messages.Config diff --git a/Nitra/ClientServer/Nitra.ClientServer.Client/StringManager.n b/Nitra/ClientServer/Nitra.ClientServer.Client/StringManager.n index 3d41b5f71..ea9ae11d9 100644 --- a/Nitra/ClientServer/Nitra.ClientServer.Client/StringManager.n +++ b/Nitra/ClientServer/Nitra.ClientServer.Client/StringManager.n @@ -25,16 +25,19 @@ namespace Nitra.ClientServer.Client public GetId(path : string) : int { - if (string.IsNullOrEmpty(path)) + def unesc = Uri.UnescapeDataString(path); + def uri = Uri(unesc).ToString(); + + if (string.IsNullOrEmpty(uri)) 0 else { mutable id; - unless (_pathToId.TryGetValue(path, out id)) + unless (_pathToId.TryGetValue(uri, out id)) { id = _idToPaths.Count; - _pathToId.Add(path, id); - _idToPaths.Add(path); + _pathToId.Add(uri, id); + _idToPaths.Add(uri); } id } diff --git a/Nitra/ClientServer/Nitra.ClientServer.Messages/Nitra.ClientServer.Messages.nproj b/Nitra/ClientServer/Nitra.ClientServer.Messages/Nitra.ClientServer.Messages.nproj index fe3814f4a..efc2614d2 100644 --- a/Nitra/ClientServer/Nitra.ClientServer.Messages/Nitra.ClientServer.Messages.nproj +++ b/Nitra/ClientServer/Nitra.ClientServer.Messages/Nitra.ClientServer.Messages.nproj @@ -61,6 +61,9 @@ + + Code + Code diff --git a/Nitra.TestsLauncher/SerializationHelper.n b/Nitra/ClientServer/Nitra.ClientServer.Messages/SerializationHelper.n similarity index 91% rename from Nitra.TestsLauncher/SerializationHelper.n rename to Nitra/ClientServer/Nitra.ClientServer.Messages/SerializationHelper.n index acc56b5d1..8ba16470d 100644 --- a/Nitra.TestsLauncher/SerializationHelper.n +++ b/Nitra/ClientServer/Nitra.ClientServer.Messages/SerializationHelper.n @@ -1,75 +1,70 @@ -using Nemerle; -using Nemerle.Collections; -using Nemerle.Extensions; -using Nemerle.Text; -using Nemerle.Utility; - -using Nitra.ClientServer.Client; -using Nitra.ViewModels; - -using System; -using System.Collections.Generic; -using System.Collections.Immutable; -using System.IO; -using System.Linq; -using System.Reflection; -using System.Xml.Serialization; - -using Messages = Nitra.ClientServer.Messages; - -namespace Nitra.Visualizer.Serialization -{ - public sealed class Language - { - [XmlAttribute] public Name: string { get; set } - [XmlAttribute] public Path: string { get; set } - public DynamicExtensions: array[DynamicExtension] { get; set } - public Libs: array[string] { get; set } - } - - public sealed class DynamicExtension - { - [XmlAttribute] public Name : string { get; set } - [XmlAttribute] public Path : string { get; set } - - public ToDynamicExtensionInfo(rootPath : string) : Messages.DynamicExtensionInfo - { - Messages.DynamicExtensionInfo(Name, Path.GetFullPath(Path.Combine(rootPath, Path))) - } - } - - public module SerializationHelper - { - _configXmlSerializer : XmlSerializer = XmlSerializer(typeof(Messages.Config)); - _languageXmlSerializer : XmlSerializer = XmlSerializer(typeof(Language)); - - public Serialize(config : Messages.Config) : string - { - def writer = StringWriter(); - _configXmlSerializer.Serialize(writer, config); - writer.ToString() - } - - public Deserialize(configPath : string) : Messages.Config - { - def text = File.ReadAllText(configPath); - def reader = StringReader(text); - - def result = - try _configXmlSerializer.Deserialize(reader) :> Messages.Config - catch - { | _e is InvalidOperationException => - // Try interpret config as old version. Try to read "Language" type - def reader = StringReader(text); - def lang = _languageXmlSerializer.Deserialize(reader) :> Language; - // Convert it to new format (Messages.Config type) - def dynExt = lang.DynamicExtensions?.Select(e => Messages.DynamicExtensionInfo(e.Name, e.Path))?.ToArray(); - def langInfo = Messages.LanguageInfo(lang.Name, lang.Path, dynExt ?? array(0)); - def result = Messages.Config(Messages.ProjectSupport("", "", ""), array[langInfo], lang.Libs?.ToArray() ?? array(0)); - File.WriteAllText(configPath, Serialize(result)); - Deserialize(configPath) - }; - result - } - } -} \ No newline at end of file +using Nemerle; +using Nemerle.Collections; +using Nemerle.Extensions; +using Nemerle.Text; +using Nemerle.Utility; + +using System; +using System.Collections.Generic; +using System.Collections.Immutable; +using System.IO; +using System.Linq; +using System.Reflection; +using System.Xml.Serialization; + +namespace Nitra.ClientServer.Messages +{ + public sealed class Language + { + [XmlAttribute] public Name: string { get; set } + [XmlAttribute] public Path: string { get; set } + public DynamicExtensions: array[DynamicExtension] { get; set } + public Libs: array[string] { get; set } + } + + public sealed class DynamicExtension + { + [XmlAttribute] public Name : string { get; set } + [XmlAttribute] public Path : string { get; set } + + public ToDynamicExtensionInfo(rootPath : string) : Messages.DynamicExtensionInfo + { + Messages.DynamicExtensionInfo(Name, Path.GetFullPath(Path.Combine(rootPath, Path))) + } + } + + public module SerializationHelper + { + _configXmlSerializer : XmlSerializer = XmlSerializer(typeof(Messages.Config)); + _languageXmlSerializer : XmlSerializer = XmlSerializer(typeof(Language)); + + public Serialize(config : Messages.Config) : string + { + def writer = StringWriter(); + _configXmlSerializer.Serialize(writer, config); + writer.ToString() + } + + public Deserialize(configPath : string) : Messages.Config + { + def text = File.ReadAllText(configPath); + def reader = StringReader(text); + + def result = + try _configXmlSerializer.Deserialize(reader) :> Messages.Config + catch + { | _e is InvalidOperationException => + // Try interpret config as old version. Try to read "Language" type + def reader = StringReader(text); + def lang = _languageXmlSerializer.Deserialize(reader) :> Language; + // Convert it to new format (Messages.Config type) + def dynExt = lang.DynamicExtensions?.Select(e => Messages.DynamicExtensionInfo(e.Name, e.Path))?.ToArray(); + def langInfo = Messages.LanguageInfo(lang.Name, lang.Path, dynExt ?? array(0)); + def result = Messages.Config(Messages.ProjectSupport("", "", ""), array[langInfo], lang.Libs?.ToArray() ?? array(0)); + File.WriteAllText(configPath, Serialize(result)); + Deserialize(configPath) + }; + result + } + } +} diff --git a/Nitra/ClientServer/Nitra.ClientServer.Server/Lsp/LspProtocolAdapter.n b/Nitra/ClientServer/Nitra.ClientServer.Server/Lsp/LspProtocolAdapter.n index 5abb47561..7eb93239c 100644 --- a/Nitra/ClientServer/Nitra.ClientServer.Server/Lsp/LspProtocolAdapter.n +++ b/Nitra/ClientServer/Nitra.ClientServer.Server/Lsp/LspProtocolAdapter.n @@ -42,8 +42,9 @@ namespace Nitra.ClientServer.Server.Lsp private mutable _output : Stream; private mutable _reader : LspReader; private mutable _writer : LspWriter; - private mutable _solution : LspSolution; + //private mutable _solution : LspSolution; private mutable _requestCounter : int; + private mutable _workspace : LspWorkspace; private mutable _pendingCompletionIdLock : object = object(); private volatile mutable _pendingCompletionId : object; @@ -66,6 +67,7 @@ namespace Nitra.ClientServer.Server.Lsp _reader.UnknownRequestHandler += UnknownHandler; + _reader.RegisterHandler("$/fileActivatedNotification", FileActivated); _reader.RegisterHandler("$/cancelRequest", CancelRequest); _reader.RegisterHandler(Methods.Initialize, Initialize); _reader.RegisterHandler(Methods.Shutdown, Shutdown); @@ -83,6 +85,7 @@ namespace Nitra.ClientServer.Server.Lsp _reader.RegisterHandler(Methods.TextDocumentDidSave, TextDocument_DidSave); _reader.RegisterHandler(Methods.WorkspaceDidChangeConfiguration, Workspace_DidChangeConfiguration); _reader.RegisterHandler(Methods.WorkspaceDidChangeWatchedFiles, Workspace_DidChangeWatchedFiles); + } public Dispose() : void @@ -110,13 +113,16 @@ namespace Nitra.ClientServer.Server.Lsp match (message) { - | FindSymbolReferences(_span, symbols, tag) => CompleteRequest(tag, symbols) - | Hint(text, _span, tag) => CompleteRequest(tag, text) - | CompleteWord(id, _span, completionList) => CompleteWord(message.FileId, completionList, id) + | FindSymbolReferences(_span, symbols, tag) => CompleteRequest(tag, symbols); + | Hint(text, _span, tag) => CompleteRequest(tag, text); + | CompleteWord(id, _span, completionList) => CompleteWord(message.FileId, completionList, id); //| ProjectLoadingMessages (projectId, messages) => SendCompilerMessagesToClient(projectId, message.FileId, messages); | ParsingMessages (projectId, messages) => lock (_locker) SendCompilerMessagesToClient(projectId, message.FileId, messages, pass=0); | MappingMessages (projectId, messages) => lock (_locker) SendCompilerMessagesToClient(projectId, message.FileId, messages, pass=1); | SemanticAnalysisMessages(projectId, messages) => lock (_locker) SendCompilerMessagesToClient(projectId, message.FileId, messages, pass=2); + | KeywordsHighlightingCreated as khc => WriteNotification(LspNotification("$/keywordHighlight"), new (uri = Uri(_workspace.StringManager.GetPath(khc.FileId)), spanInfos = khc.spanInfos)); + | SymbolsHighlightingCreated as shc => WriteNotification(LspNotification("$/symbolHighlight"), new (uri = Uri(_workspace.StringManager.GetPath(shc.FileId)), spanInfos = shc.spanInfos)); + | LanguageLoaded as ll => WriteNotification(LspNotification("$/languageLoaded"), new (SpanClassInfo = ll.spanClassInfos)); | _ => () } } @@ -129,34 +135,34 @@ namespace Nitra.ClientServer.Server.Lsp return; def diagnosticsPass = - if (pass == 0) - { - def x = array[array(0), array(0), array(0)]; - _fileToCompilerMessageMap[fileId] = x; - x - } - else - _fileToCompilerMessageMap.GetOrAdd(fileId, (_) => array[array(0), array(0), array(0)]); + if (pass == 0) + { + def x = array[array(0), array(0), array(0)]; + _fileToCompilerMessageMap[fileId] = x; + x + } + else + _fileToCompilerMessageMap.GetOrAdd(fileId, (_) => array[array(0), array(0), array(0)]); def diagnostics = List(); - def uri = _solution.GetFileUri(fileId); + def uri = _workspace.StringManager.GetPath(fileId); foreach (m in messages) { def severity = - match (m.Type) - { - | FatalError => DiagnosticSeverity.Error - | Error => DiagnosticSeverity.Error - | Warning => DiagnosticSeverity.Warning - | Hint => DiagnosticSeverity.Hint - }; + match (m.Type) + { + | FatalError => DiagnosticSeverity.Error + | Error => DiagnosticSeverity.Error + | Warning => DiagnosticSeverity.Warning + | Hint => DiagnosticSeverity.Hint + }; def loc = m.Location; diagnostics.Add(Diagnostic() <- { - Range = Convert(loc.Range); - Severity = severity; - Code = if (m.Number < 0) null else m.Number.ToString(); - Message = m.Text; + Range = Convert(loc.Range); + Severity = severity; + Code = if (m.Number < 0) null else m.Number.ToString(); + Message = m.Text; // Source = ??? }); } @@ -168,7 +174,7 @@ namespace Nitra.ClientServer.Server.Lsp diagnostics.AddRange(diagnosticsPass[1]); diagnostics.AddRange(diagnosticsPass[2]); - def @params = PublishDiagnosticParams() <- { Uri = uri; Diagnostics = diagnostics.ToArray(); }; + def @params = PublishDiagnosticParams() <- { Uri = Uri(uri); Diagnostics = diagnostics.ToArray(); }; WriteNotification(Methods.TextDocumentPublishDiagnostics, @params); Log.Message($"LSP < textDocument/publishDiagnostics fileId=$fileId pass=$pass messages.Length=$(messages.Length) diagnostics", ConsoleColor.DarkCyan); } @@ -180,6 +186,84 @@ namespace Nitra.ClientServer.Server.Lsp WriteError(id, LspErrorCode.MethodNotFound, method) } + private FileActivated(doc: TextDocumentItem): void + { + when(!doc.Uri.ToString().EndsWith(".tdl")) + return; + + def fId = _workspace.GetFid(doc.Uri.ToString()); + def sidOpt = _workspace.GetSid(fId); + + when(sidOpt.IsNone) + return; + + def sId = sidOpt.GetValueOrDefault(); + + def prevActive = _workspace.ActiveSolutionId; + _ = prevActive; + + when(sId != _workspace.ActiveSolutionId && sId != 0) + { + when(_workspace.ActiveSolutionId != SolutionId(0)) + _router.AddMessage(ClientMessage.SolutionUnloaded(_workspace.ActiveSolutionId)); + + def solOpt = _workspace.GetS(sId); + when(solOpt.IsNone) + return; + + def sol = solOpt.GetValueOrDefault(); + + def sId = SolutionId(_workspace.StringManager.GetId(sol.Path)); + def msg = ClientMessage.SolutionStartLoading(sId, Uri(sol.Path).LocalPath); + _router.AddMessage(msg); + + sol.FileToProjectMap + .Select(x => x.Value) + .Iter(p => + { + def pId = ProjectId(_workspace.StringManager.GetId(p.Path)); + def msg = ClientMessage.ProjectStartLoading(pId, Uri(p.Path).LocalPath, p.Config); + _router.AddMessage(msg); + + p.Config.References + .Iter(r => + { + def msg = ClientMessage.ReferenceLoaded(pId, r); + _router.AddMessage(msg); + }); + + p.Files.Select(x => x.Value) + .Iter(f => + { + def fId = FileId(_workspace.StringManager.GetId(f.Path)); + def msg = ClientMessage.FileLoaded(pId, Uri(f.Path).LocalPath, fId, version = FileVersion(1), hasContent = false, contentOpt = null); + _router.AddMessage(msg); + }); + + def msg = ClientMessage.ProjectLoaded(pId); + _router.AddMessage(msg); + + }); + + def msg = ClientMessage.SolutionLoaded(sId); + _router.AddMessage(msg); + + _workspace.ActiveSolutionId = sId; + } + + def pidOpt = _workspace.GetPid(fId); + when(pidOpt.IsSome) + { + def msg = ClientMessage.FileActivated(pidOpt.GetValueOrDefault(), fId, FileVersion(doc.Version)); + _router.AddMessage(msg); + } + + Log.Message($"LSP -> $$/fileActivatedNotification file=$(doc.Uri)", ConsoleColor.Cyan); + //def fileId = _solution.GetFileId(doc.Uri); + //foreach (project in _solution.GetProjects(fileId)) + // _router.AddMessage(ClientMessage.FileActivated(project.ProjectId, fileId, FileVersion(doc.Version))); + } + private CancelRequest(p : CancelParams) : void { Log.Message($"LSP -> $$/cancelRequest Id=$(p.Id)", ConsoleColor.Cyan); @@ -195,26 +279,27 @@ namespace Nitra.ClientServer.Server.Lsp def result = InitializeResult() <- { - Capabilities = ServerCapabilities() <- + Capabilities = ServerCapabilities() <- { - TextDocumentSync = TextDocumentSyncOptions() <- + TextDocumentSync = TextDocumentSyncOptions() <- { - OpenClose = true; - Change = TextDocumentSyncKind.Incremental; - Save = SaveOptions() <- { IncludeText = false } + OpenClose = true; + Change = TextDocumentSyncKind.Incremental; + Save = SaveOptions() <- { IncludeText = false } }; - DefinitionProvider = true; - ReferencesProvider = true; - DocumentHighlightProvider = true; - CompletionProvider = CompletionOptions() <- { ResolveProvider = false }; - HoverProvider = true + DefinitionProvider = true; + ReferencesProvider = true; + DocumentHighlightProvider = true; + CompletionProvider = CompletionOptions() <- { ResolveProvider = false }; + HoverProvider = true } }; when (!string.IsNullOrEmpty(p.RootPath) && IO.Directory.Exists(p.RootPath)) { - def options = (p.InitializationOptions :> JObject).ToObject.[InitializationOptions](); - LoadSolution(p.RootPath, options) + //def options = (p.InitializationOptions :> JObject).ToObject.[InitializationOptions](); + + _workspace = LspWorkspace(p.RootPath); } WriteResult(id, result); @@ -224,7 +309,7 @@ namespace Nitra.ClientServer.Server.Lsp { Log.Message("LSP > initialized", ConsoleColor.Cyan); def make(pattern : string) { FileSystemWatcher() <- { GlobPattern = pattern; Kind = WatchKind.Create | WatchKind.Change | WatchKind.Delete; } } - def watchers = _solution.GetWatchedPatterns().Select(make).ToArray(); + def watchers = [".tdl"].Select(make).ToArray(); def options = DidChangeWatchedFilesRegistrationOptions() <- { Watchers = watchers; }; def registrationParams = RegistrationParams() <- { Registrations = array[ Registration() <- @@ -256,51 +341,65 @@ namespace Nitra.ClientServer.Server.Lsp private TextDocument_DidOpen(p : DidOpenTextDocumentParams) : void { - def document = p.TextDocument; - def uri = document.Uri; - def fileId = _solution.GetFileId(uri); - def version = FileVersion(document.Version); - def path = ServerUtils.TrimBeginSlashInFullyQualifiedWindowsPath(uri.LocalPath); - Log.Message($"LSP > textDocument/didOpen fileId=$fileId Version=$(document.Version) Uri='$uri' Text=«$(document.Text)»", ConsoleColor.Cyan); - foreach (project in _solution.GetProjects(fileId)) - project.DidOpenFileForEditing(fileId, version, path, document.Text); - otherwise - TryAddToProject(fileId, version, path, document.Text); - } - - private TryAddToProject(fileId : FileId, version : FileVersion, path : string, text : string) : void - { - def matchedProjects = _solution.FindMatchingProjects(fileId).ToList(); - foreach (project in matchedProjects) - project.DidOpenFileForEditing(fileId, version, path, text); - otherwise - { - def project = _solution.OpenProject(path); - project.DidOpenFileForEditing(fileId, version, path, text); - } - } - - private TryAddToProject(fileId : FileId, path : string) : void - { - def matchedProjects = _solution.FindMatchingProjects(fileId).ToList(); - Log.Message($<#LSP TryAddToProject fileId=$fileId matchedProjects=[..$matchedProjects] path=$path")#>, ConsoleColor.Cyan); - - foreach (project in matchedProjects) - project.LoadFile(fileId, path); - otherwise - { - def project = _solution.OpenProject(path); - project.LoadFile(fileId, path); - } - } + //when(_workspace.AddFile(p.TextDocument.Uri.ToString())) + //{ + // def fId = FileId(_workspace.StringManager.GetId(p.TextDocument.Uri.ToString())); + // def pId = _workspace.GetPid(fId); + // _router.AddMessage(ClientMessage.FileLoaded(pId + // , p.TextDocument.Uri.LocalPath + // , fId + // , FileVersion(p.TextDocument.Version) + // , hasContent = false, contentOpt = null)); + //} + + //FileActivated(p.TextDocument); + + //def document = p.TextDocument; + //def uri = document.Uri; + //def fileId = FileId(_workspace.StringManager.GetId(uri.AbsolutePath)); + //def version = FileVersion(document.Version); + //def path = ServerUtils.TrimBeginSlashInFullyQualifiedWindowsPath(uri.LocalPath); + //Log.Message($"LSP > textDocument/didOpen fileId=$fileId Version=$(document.Version) Uri='$uri' Text=«$(document.Text)»", ConsoleColor.Cyan); + //foreach (project in _.GetProjects(fileId)) + // project.DidOpenFileForEditing(fileId, version, path, document.Text); + //otherwise + // TryAddToProject(fileId, version, path, document.Text); + } + + //private TryAddToProject(fileId : FileId, version : FileVersion, path : string, text : string) : void + //{ + // def matchedProjects = _solution.FindMatchingProjects(fileId).ToList(); + // foreach (project in matchedProjects) + // project.DidOpenFileForEditing(fileId, version, path, text); + // otherwise + // { + // def project = _solution.OpenProject(path); + // project.DidOpenFileForEditing(fileId, version, path, text); + // } + //} + + //private TryAddToProject(fileId : FileId, path : string) : void + //{ + // def matchedProjects = _solution.FindMatchingProjects(fileId).ToList(); + // Log.Message($<#LSP TryAddToProject fileId=$fileId matchedProjects=[..$matchedProjects] path=$path")#>, ConsoleColor.Cyan); + + // foreach (project in matchedProjects) + // project.LoadFile(fileId, path); + // otherwise + // { + // def project = _solution.OpenProject(path); + // project.LoadFile(fileId, path); + // } + //} private TextDocument_DidClose(p : DidCloseTextDocumentParams) : void { - def fileId = _solution.GetFileId(p.TextDocument.Uri); + def fId = FileId(_workspace.StringManager.GetId(p.TextDocument.Uri.ToString())); Log.Message($"LSP > textDocument/didClose $(p.TextDocument.Uri)" + p.TextDocument.Uri, ConsoleColor.Cyan); - foreach (project in _solution.GetProjects(fileId)) - project.DidClose(fileId); - //_router.AddMessage(ClientMessage.FileDeactivated(project.ProjectId, fileId)); + //def pId = _workspace.ActiveSolution.FileToProjectMap[fId]; + def pidOpt = _workspace.GetPid(fId); + when(pidOpt.IsSome) + _router.AddMessage(ClientMessage.FileDeactivated(pidOpt.GetValueOrDefault(), fId)); } private static ToStringDidChangeTextDocumentParamsToString(msg : DidChangeTextDocumentParams) : string @@ -322,52 +421,73 @@ Version: $(msg.TextDocument.Version) Uri: '$(msg.TextDocument.Uri)' def document = p.TextDocument; def changes = p.ContentChanges; - def fileId = _solution.GetFileId(document.Uri); + def fileId = FileId(_workspace.StringManager.GetId(document.Uri.ToString())); def version = FileVersion(document.Version ?? -1); def message = - if (changes.Length == 1) - { - ClientMessage.FileChanged(fileId, version, Convert(changes[0]), VersionedPos(Convert(changes[0].Range.End), version)) - } - else - { - def builder = ImmutableArray.CreateBuilder(changes.Length); - for (mutable i = changes.Length - 1; i >= 0; i--) - builder.Add(Convert(changes[i])); - ClientMessage.FileChangedBatch(fileId, version, builder.MoveToImmutable(), VersionedPos.Invalid); - } + if (changes.Length == 1) + { + ClientMessage.FileChanged(fileId, version, Convert(changes[0]), VersionedPos(Convert(changes[0].Range.End), version)) + } + else + { + def builder = ImmutableArray.CreateBuilder(changes.Length); + for (mutable i = changes.Length - 1; i >= 0; i--) + builder.Add(Convert(changes[i])); + ClientMessage.FileChangedBatch(fileId, version, builder.MoveToImmutable(), VersionedPos.Invalid); + } _router.AddMessage(message); } private Workspace_DidChangeWatchedFiles(p : DidChangeWatchedFilesParams) : void { - Log.Message($<#LSP > $(Methods.WorkspaceDidChangeWatchedFilesName) ..$(p.Changes; ", "; c => $"FileChangeType=$(c.FileChangeType) fileId=$( _solution.GetFileId(c.Uri)) Uri=$(c.Uri.LocalPath)")#>, ConsoleColor.Cyan); + Log.Message($<#LSP > $(Methods.WorkspaceDidChangeWatchedFilesName) ..$(p.Changes; ", ";c => $"FileChangeType=$(c.FileChangeType) fileId=$( _workspace.StringManager.GetId(c.Uri.ToString())) Uri=$(c.Uri.ToString())")#>, ConsoleColor.Cyan); foreach (change in p.Changes) { - def fileId = _solution.GetFileId(change.Uri); - foreach (project in _solution.GetProjects(fileId)) + def filePath = change.Uri.ToString(); + def fId = _workspace.GetFid(filePath); + + def msg = match (change.FileChangeType) { - match (change.FileChangeType) - { - | FileChangeType.Created => project.OnCreatedFile(fileId); - | FileChangeType.Deleted => project.OnDeletedFile(fileId); // TODO: Если файл открыт на редактирование, то переложить его в отдельный, временный, проект. - | FileChangeType.Changed => project.OnExternallyChangedFile(fileId); - } + | (FileChangeType.Created) when _workspace.AddFile(filePath) => + { + def pidOpt = _workspace.GetPid(fId); + if(pidOpt.IsSome) + Some(ClientMessage.FileLoaded(pidOpt.GetValueOrDefault(), filePath, fId, FileVersion(1), false, null)); + else None() + } + // TODO: Если файл открыт на редактирование, то переложить его в отдельный, временный, проект. + | (FileChangeType.Deleted) when _workspace.RemoveFile(filePath) => + { + def pidOpt = _workspace.GetPid(fId); + if(pidOpt.IsSome) + Some(ClientMessage.FileUnloaded(pidOpt.GetValueOrDefault(), fId)); + else + None(); + } + //| (_, FileChangeType.Changed) => None(); + | _ => None(); } - otherwise + //otherwise + //{ + // Log.Message($<#LSP File is not part of any project. FileChangeType=$(change.FileChangeType) fileId=$fileId Uri=$(change.Uri.LocalPath)")#>, ConsoleColor.Cyan); + // match (change.FileChangeType) + // { + // | FileChangeType.Created => TryAddToProject(fileId, ServerUtils.TrimBeginSlashInFullyQualifiedWindowsPath(change.Uri.LocalPath)); + // | FileChangeType.Deleted => Log.Error($"File not in any project! fileId=$fileId FileChangeType=$(change.FileChangeType) Uri=$(change.Uri.LocalPath)"); + // | FileChangeType.Changed => Log.Error($"File not in any project! fileId=$fileId FileChangeType=$(change.FileChangeType) Uri=$(change.Uri.LocalPath)"); + // } + //} + match(msg) { - Log.Message($<#LSP File is not part of any project. FileChangeType=$(change.FileChangeType) fileId=$fileId Uri=$(change.Uri.LocalPath)")#>, ConsoleColor.Cyan); - match (change.FileChangeType) - { - | FileChangeType.Created => TryAddToProject(fileId, ServerUtils.TrimBeginSlashInFullyQualifiedWindowsPath(change.Uri.LocalPath)); - | FileChangeType.Deleted => Log.Error($"File not in any project! fileId=$fileId FileChangeType=$(change.FileChangeType) Uri=$(change.Uri.LocalPath)"); - | FileChangeType.Changed => Log.Error($"File not in any project! fileId=$fileId FileChangeType=$(change.FileChangeType) Uri=$(change.Uri.LocalPath)"); - } + | Some(m) => _router.AddMessage(m); + | None => (); } } } + + private TextDocument_DidSave(p : DidSaveTextDocumentParams) : void { Log.Message($"LSP > textDocument/didSave Uri=$(p.TextDocument.Uri) «$(p.Text)»", ConsoleColor.Cyan); @@ -376,51 +496,56 @@ Version: $(msg.TextDocument.Version) Uri: '$(msg.TextDocument.Uri)' private TextDocument_DocumentHighlight(p : TextDocumentPositionParams, id : object) : void { def document = p.TextDocument; - def fileId = _solution.GetFileId(document.Uri); + def fId = FileId(_workspace.StringManager.GetId(document.Uri.ToString())); def position = VersionedPos(Convert(p.Position), FileVersion.Invalid); - Log.Message($"LSP -> textDocument/documentHighlight id=$id fileId=$fileId Line=$(p.Position.Line) Character=$(p.Position.Character) Uri=$(document.Uri)", ConsoleColor.Cyan); - when (_solution.TryGetFirstProjects(fileId) is VSome(project)) - _router.AddMessage(ClientMessage.FindSymbolReferences(project.ProjectId, fileId, position, true, RegisterRequest(id, DocumentHighlight))); + Log.Message($"LSP -> textDocument/documentHighlight id=$id fileId=$fId Line=$(p.Position.Line) Character=$(p.Position.Character) Uri=$(document.Uri)", ConsoleColor.Cyan); + def pidOpt = _workspace.GetPid(fId); + when(pidOpt.IsSome) + _router.AddMessage(ClientMessage.FindSymbolReferences(pidOpt.GetValueOrDefault(), fId, position, true, RegisterRequest(id, DocumentHighlight))); } private TextDocument_References(p : ReferenceParams, id : object) : void { def document = p.TextDocument; - def fileId = _solution.GetFileId(document.Uri); + def fId = FileId(_workspace.StringManager.GetId(document.Uri.ToString())); def position = VersionedPos(Convert(p.Position), FileVersion.Invalid); - Log.Message($"LSP -> textDocument/references id=$id fileId=$fileId Line=$(p.Position.Line) Character=$(p.Position.Character) Uri=$(document.Uri)", ConsoleColor.Cyan); - when (_solution.TryGetFirstProjects(fileId) is VSome(project)) - _router.AddMessage(ClientMessage.FindSymbolReferences(project.ProjectId, fileId, position, false, RegisterRequest(id, FindReferences(_, _, p.Context.IncludeDeclaration)))); + Log.Message($"LSP -> textDocument/references id=$id fileId=$fId Line=$(p.Position.Line) Character=$(p.Position.Character) Uri=$(document.Uri)", ConsoleColor.Cyan); + def pidOpt = _workspace.GetPid(fId); + when(pidOpt.IsSome) + _router.AddMessage(ClientMessage.FindSymbolReferences(pidOpt.GetValueOrDefault(), fId, position, false, RegisterRequest(id, FindReferences(_, _, p.Context.IncludeDeclaration)))); } private TextDocument_Hover(p : TextDocumentPositionParams, id : object) : void { def document = p.TextDocument; - def fileId = _solution.GetFileId(document.Uri); + def fId = FileId(_workspace.StringManager.GetId(document.Uri.ToString())); def position = VersionedPos(Convert(p.Position), FileVersion.Invalid); - Log.Message($"LSP -> textDocument/hover id=$id fileId=$fileId Line=$(p.Position.Line) Character=$(p.Position.Character) Uri=$(document.Uri)", ConsoleColor.Cyan); - when (_solution.TryGetFirstProjects(fileId) is VSome(project)) - _router.AddMessage(ClientMessage.GetHint(project.ProjectId, fileId, position, RegisterRequest(id, ShowHint(_, _, p.Position)))); + Log.Message($"LSP -> textDocument/hover id=$id fileId=$fId Line=$(p.Position.Line) Character=$(p.Position.Character) Uri=$(document.Uri)", ConsoleColor.Cyan); + def pidOpt = _workspace.GetPid(fId); + when(pidOpt.IsSome) + _router.AddMessage(ClientMessage.GetHint(pidOpt.GetValueOrDefault(), fId, position, RegisterRequest(id, ShowHint(_, _, p.Position)))); } private TextDocument_Completion(p : CompletionParams, id : object) : void { def document = p.TextDocument; - def fileId = _solution.GetFileId(document.Uri); + def fId = FileId(_workspace.StringManager.GetId(document.Uri.ToString())); def position = VersionedPos(Convert(p.Position), FileVersion.Invalid); - Log.Message($"LSP -> textDocument/completion id=$id fileId=$fileId Line=$(p.Position.Line) Character=$(p.Position.Character) TriggerKind=$(p.Context.TriggerKind) TriggerCharacter=$(p.Context.TriggerCharacter) Uri=$(document.Uri)", ConsoleColor.Cyan); - when (_solution.TryGetFirstProjects(fileId) is VSome(project)) - _router.AddMessage(ClientMessage.CompleteWord((id :> long) :> int, project.ProjectId, fileId, position)); + Log.Message($"LSP -> textDocument/completion id=$id fileId=$fId Line=$(p.Position.Line) Character=$(p.Position.Character) TriggerKind=$(p.Context.TriggerKind) TriggerCharacter=$(p.Context.TriggerCharacter) Uri=$(document.Uri)", ConsoleColor.Cyan); + def pidOpt = _workspace.GetPid(fId); + when(pidOpt.IsSome) + _router.AddMessage(ClientMessage.CompleteWord((id :> long) :> int, pidOpt.GetValueOrDefault(), fId, position)); } private TextDocument_Definition(p : TextDocumentPositionParams, id : object) : void { def document = p.TextDocument; - def fileId = _solution.GetFileId(document.Uri); + def fId = FileId(_workspace.StringManager.GetId(document.Uri.ToString())); def position = VersionedPos(Convert(p.Position), FileVersion.Invalid); - Log.Message($"LSP -> textDocument/definition id=$id fileId=$fileId Line=$(p.Position.Line) Character=$(p.Position.Character) Uri=$(document.Uri)", ConsoleColor.Cyan); - when (_solution.TryGetFirstProjects(fileId) is VSome(project)) - _router.AddMessage(ClientMessage.FindSymbolDefinitions(project.ProjectId, fileId, position, RegisterRequest(id, FindDefinitions(_, _)))); + Log.Message($"LSP -> textDocument/definition id=$id fileId=$fId Line=$(p.Position.Line) Character=$(p.Position.Character) Uri=$(document.Uri)", ConsoleColor.Cyan); + def pidOpt = _workspace.GetPid(fId); + when(pidOpt.IsSome) + _router.AddMessage(ClientMessage.FindSymbolDefinitions(pidOpt.GetValueOrDefault(), fId, position, RegisterRequest(id, FindDefinitions(_, _)))); } private WriteError(id : object, code : LspErrorCode, message : string) : void @@ -450,13 +575,13 @@ Version: $(msg.TextDocument.Version) Uri: '$(msg.TextDocument.Uri)' } } - private LoadSolution(path : string, options : InitializationOptions) : void - { - _solution = LspSolution(_router, SolutionId(0), options.Config, ImmutableArray.Create(options.FileExtension)); // TODO: Add support of multiple extensions - _router.AddMessage(ClientMessage.SolutionStartLoading(_solution.SolutionId, path)); - _ = _solution.OpenProject(path); // TODO: Add support of multiproject - _router.AddMessage(ClientMessage.SolutionLoaded(_solution.SolutionId)); - } + //private LoadSolution(path : string, options : InitializationOptions) : void + //{ + // _solution = LspSolution(_router, SolutionId(0), options.Config, ImmutableArray.Create(options.FileExtension)); // TODO: Add support of multiple extensions + // _router.AddMessage(ClientMessage.SolutionStartLoading(_solution.SolutionId, path)); + // _ = _solution.OpenProject(path); // TODO: Add support of multiproject + // _router.AddMessage(ClientMessage.SolutionLoaded(_solution.SolutionId)); + //} private DocumentHighlight(symbols : ImmutableArray[SymbolReferences], id : object) : void { @@ -465,8 +590,8 @@ Version: $(msg.TextDocument.Version) Uri: '$(msg.TextDocument.Uri)' { result.Add(DocumentHighlight() <- { - Range = range; - Kind = DocumentHighlightKind.Text + Range = range; + Kind = DocumentHighlightKind.Text }); } WriteResult(id, result); @@ -479,8 +604,8 @@ Version: $(msg.TextDocument.Version) Uri: '$(msg.TextDocument.Uri)' { result.Add(LSP.Location() <- { - Range = range; - Uri = _solution.GetFileUri(fileId) + Range = range; + Uri = Uri(_workspace.StringManager.GetPath(fileId)); }); } WriteResult(id, result); @@ -493,8 +618,8 @@ Version: $(msg.TextDocument.Version) Uri: '$(msg.TextDocument.Uri)' { result.Add(LSP.Location() <- { - Range = range; - Uri = _solution.GetFileUri(fileId) + Range = range; + Uri = Uri(_workspace.StringManager.GetPath(fileId)); }); } WriteResult(id, result); @@ -506,8 +631,8 @@ Version: $(msg.TextDocument.Version) Uri: '$(msg.TextDocument.Uri)' WriteResult(id, Hover() <- { - Contents = new (kind = "markdown", value = value); // TODO: transform XML to Markdown - Range = LSP.Range() <- { Start = position; End = LSP.Position(position.Line, position.Character + 1) } + Contents = new (kind = "markdown", value = value); // TODO: transform XML to Markdown + Range = LSP.Range() <- { Start = position; End = LSP.Position(position.Line, position.Character + 1) } }); } @@ -544,7 +669,7 @@ Version: $(msg.TextDocument.Version) Uri: '$(msg.TextDocument.Uri)' { result.Add(LSP.CompletionItem() <- { - Label = x.Text + Label = x.Text }); } WriteResult(id, result); @@ -554,8 +679,8 @@ Version: $(msg.TextDocument.Version) Uri: '$(msg.TextDocument.Uri)' { Range() <- { - Start = Position() <- { Line = range.StartLine - 1; Character = range.StartColumn - 1 }; - End = Position() <- { Line = range.EndLine - 1; Character = range.EndColumn - 1 } + Start = Position() <- { Line = range.StartLine - 1; Character = range.StartColumn - 1 }; + End = Position() <- { Line = range.EndLine - 1; Character = range.EndColumn - 1 } } } @@ -579,21 +704,21 @@ Version: $(msg.TextDocument.Version) Uri: '$(msg.TextDocument.Uri)' } foreach (r in s.References) - foreach (g in r.Ranges) - yield (Convert(g), r.File.FileId); + foreach (g in r.Ranges) + yield (Convert(g), r.File.FileId); } } private static Convert(c : TextDocumentContentChangeEvent) : FileChange { - def newText = c.Text; - def isEmpty = string.IsNullOrEmpty(newText); - if (c.RangeLength == 0 && !isEmpty) - FileChange.Insert(Convert(c.Range.Start), newText) - else if (c.RangeLength > 0 && isEmpty) - FileChange.Delete(Convert(c.Range)) - else - FileChange.Replace(Convert(c.Range), newText) + def newText = c.Text; + def isEmpty = string.IsNullOrEmpty(newText); + if (c.RangeLength == 0 && !isEmpty) + FileChange.Insert(Convert(c.Range.Start), newText) + else if (c.RangeLength > 0 && isEmpty) + FileChange.Delete(Convert(c.Range)) + else + FileChange.Replace(Convert(c.Range), newText) } private static Convert(p : Position) : UIPosition @@ -632,7 +757,7 @@ Version: $(msg.TextDocument.Version) Uri: '$(msg.TextDocument.Uri)' { def di = IO.DirectoryInfo(logDir); foreach (file in di.GetFiles()) - file.Delete(); + file.Delete(); } else _ = IO.Directory.CreateDirectory(logDir); diff --git a/Nitra/ClientServer/Nitra.ClientServer.Server/Lsp/LspWorkspace.n b/Nitra/ClientServer/Nitra.ClientServer.Server/Lsp/LspWorkspace.n new file mode 100644 index 000000000..bb870e636 --- /dev/null +++ b/Nitra/ClientServer/Nitra.ClientServer.Server/Lsp/LspWorkspace.n @@ -0,0 +1,216 @@ +using Nemerle; +using Nemerle.Collections; +using Nemerle.Text; +using Nemerle.Utility; +using Nemerle.Extensions; +using Nemerle.Imperative; + +using System; +using System.Collections.Generic; +using System.Linq; +using System.IO; + +using Nitra.ClientServer.Client; +using Nitra.ClientServer.Messages; + +using Nitra.Logging; + +namespace Nitra.ClientServer.Server.Lsp +{ + internal class LspSuite + { + public mutable Path : string; + public mutable SolutionMap : Hashtable[SolutionId, LspSolution]; + public mutable ProjectMap : Hashtable[ProjectId, LspProject]; + public mutable FileToSolutionMap : Hashtable[FileId, LspSolution]; + public mutable ConfigPath : string; + } + + internal class LspSolution + { + public mutable Path : string; + public mutable FileToProjectMap : Hashtable[FileId, LspProject]; + } + + internal class LspProject + { + public mutable Path : string; + public mutable Config : Config; + public mutable Files : Hashtable[string, LspProjectFile]; + } + + internal class LspProjectFile + { + public mutable Path : string; + public mutable ProjectPath : string; + } + + + /// + /// Description of LspWorkspace. + /// + internal class LspWorkspace + { + public StringManager : StringManager = StringManager(); + + private Suite : LspSuite; + + public mutable ActiveSolutionId : SolutionId; + + public this(rootFolder: string) + { + def solutionFile = Directory.EnumerateFiles(rootFolder) + .Where(x => x.EndsWith(".nsln")) + .FirstOrDefault(); + + def suiteFolders = File.ReadAllLines(solutionFile); + + Suite = suiteFolders.Select(x => string.Join("\\", [rootFolder, x])).Select(LoadSuite).FirstOrDefault(); // TODO: support for multiple suites ??? + + } + + private LoadSuite(path: string) : LspSuite + { + def solPaths = Directory.EnumerateDirectories(path).Select(x => Uri(x).ToString()); + + def configPath = string.Join("\\", [path, "config.xml"]); + + def solMap = Hashtable(solPaths.Select(x => LoadSolution(x, configPath)).ToDictionary(x => SolutionId(StringManager.GetId(x.Path)), x => x)); + + def fileToSolMap = Hashtable(solPaths.SelectMany(x => Directory.EnumerateFiles(Uri(x).LocalPath, "*.tdl", SearchOption.AllDirectories) + .Select(a => Uri(a).ToString()) + .Select(a => { + def fId = FileId(StringManager.GetId(a)); + def s = solMap[SolutionId(StringManager.GetId(x))]; + new (file = fId, sol = s); + })) + .ToDictionary(x => x.file, x => x.sol)); + LspSuite() <- + { + Path = path; + SolutionMap = solMap; + FileToSolutionMap = fileToSolMap; + ConfigPath = configPath + }; + } + + public GetFid(path: string) : FileId + { + FileId(StringManager.GetId(path)) + } + + public GetP(fId: FileId) : option[LspProject] + { + match(Suite.FileToSolutionMap.Get(fId)) + { + | Some(sol) => sol.FileToProjectMap.Get(fId) + | _ => None() + } + } + + public GetPid(fId: FileId) : ValueOption[ProjectId] + { + match(GetP(fId)) + { + | Some(p) => VSome(ProjectId(StringManager.GetId(p.Path))) + | _ => VNone() + } + } + + public GetP(path: string) : LspProject + { + Suite.SolutionMap.Select(x => x.Value).SelectMany(x => x.FileToProjectMap.Select(a => a.Value)) + .Where(x => path.StartsWith(x.Path)).FirstOrDefault(); + } + + public GetS(sId: SolutionId): option[LspSolution] + { + Suite.SolutionMap.Get(sId); + } + + public GetS(fId: FileId) : option[LspSolution] + { + Suite.FileToSolutionMap.Get(fId); + } + + public GetSid(fId: FileId): option[SolutionId] + { + match(GetS(fId)) + { + | Some(s) => Some(SolutionId(StringManager.GetId(s.Path))) + | _ => None() + } + } + + public GetS(path: string) : LspSolution + { + Suite.SolutionMap.Select(x => x.Value).Where(x => path.StartsWith(x.Path)).FirstOrDefault(); + } + + public LoadSolution(path: string, configPath: string) : LspSolution + { + def projectFolders = Directory.EnumerateDirectories(Uri(path).LocalPath); + + def fileToProjMap = Hashtable(projectFolders.SelectMany(x => Directory.EnumerateFiles(x, "*.tdl", SearchOption.AllDirectories) + .Select(a => new (file = StringManager.GetId(a), proj = StringManager.GetId(x)))) + .ToDictionary(x => FileId(x.file), x => LoadProject(StringManager.GetPath(x.proj), configPath))); + + LspSolution() <- { Path = path; FileToProjectMap = fileToProjMap }; + } + + public LoadProject(path: string, configPath: string) : LspProject + { + def config = SerializationHelper.Deserialize(configPath); + def files = LoadProjectFiles(path); + + LspProject() <- { Path = Uri(path).ToString(); Config = config; Files = Hashtable(files.ToDictionary(x => x.Path)) }; + } + + public LoadProjectFiles(path: string) : List[LspProjectFile] + { + Directory.EnumerateFiles((Uri(path).LocalPath), "*.tdl", SearchOption.AllDirectories) + .Select(x => LspProjectFile() <- { Path = Uri(x).ToString(); ProjectPath = Uri(path).ToString() }) + .ToList(); + } + + public AddFile(pth: string) : bool + { + def path = Uri(pth).ToString(); + + def fId = FileId(StringManager.GetId(path)); + + when(Suite.FileToSolutionMap.ContainsKey(fId)) + return false; + + def sol = GetS(path); + Suite.FileToSolutionMap[fId] = sol; + def proj = GetP(path); + + when(proj == null) + return false; + + //def pId = ProjectId(StringManager.GetId(proj.Path.ToString())); + + proj.Files[path] = LspProjectFile() <- { Path = path; ProjectPath = proj.Path }; + sol.FileToProjectMap[fId] = proj; + + true; + } + + public RemoveFile(path: string) : bool + { + def fId = FileId(StringManager.GetId(path)); + match(GetP(fId), GetS(fId)) + { + | (Some(proj), Some(sol)) when !File.Exists(path) => + { + _ = proj.Files.Remove(path); + _ = sol.FileToProjectMap.Remove(fId); + _ = Suite.FileToSolutionMap.Remove(fId); + true; + } + | _ => false; + } + } + } +} diff --git a/Nitra/ClientServer/Nitra.ClientServer.Server/Nitra.ClientServer.Server.nproj b/Nitra/ClientServer/Nitra.ClientServer.Server/Nitra.ClientServer.Server.nproj index c24f32adf..e4584c60e 100644 --- a/Nitra/ClientServer/Nitra.ClientServer.Server/Nitra.ClientServer.Server.nproj +++ b/Nitra/ClientServer/Nitra.ClientServer.Server/Nitra.ClientServer.Server.nproj @@ -106,16 +106,19 @@ Code - + Code - + Code Code - + + Code + + Code @@ -205,6 +208,11 @@ {8bffaf3a-b454-4544-9218-3197df381d88} True + + Nitra.ClientServer.Client + {43be6b32-535b-40d4-943a-0797ba1b6a90} + True + Nitra.ClientServer.Messages {7908821a-47a0-42f4-b6e5-f3062ba8a06a} diff --git a/Nitra/ClientServer/Nitra.ClientServer.Server/Router.n b/Nitra/ClientServer/Nitra.ClientServer.Server/Router.n index 6184eefd7..2cabcff92 100644 --- a/Nitra/ClientServer/Nitra.ClientServer.Server/Router.n +++ b/Nitra/ClientServer/Nitra.ClientServer.Server/Router.n @@ -129,6 +129,11 @@ namespace Nitra.ClientServer.Server } } + public Send(msg: ClientMessage): void + { + AddMessage(msg); + } + public AddMessage(msg : RouterMessage): void { when (msg is ClientMessage.SolutionStartLoading) diff --git a/Nitra/DotNetLang/Collectors/BaseTypeReferenceSet.n b/Nitra/DotNetLang/Collectors/BaseTypeReferenceSet.n index 777c56feb..68ac0f07e 100644 --- a/Nitra/DotNetLang/Collectors/BaseTypeReferenceSet.n +++ b/Nitra/DotNetLang/Collectors/BaseTypeReferenceSet.n @@ -133,7 +133,7 @@ namespace DotNet Debug.WriteLine("SupportsInheritanceTypeSymbol(IsBaseTypeSetEvaluated=true, IsMemberTableEvaluated=false)"); assert2(false); - | _ => assert2(false); + | _ => (); //assert2(false); } foreach (type in ancestorTypes)