diff --git a/compiler/NubLang/Diagnostics/Diagnostic.cs b/compiler/NubLang/Diagnostics/Diagnostic.cs index 9dd9195..fd940b4 100644 --- a/compiler/NubLang/Diagnostics/Diagnostic.cs +++ b/compiler/NubLang/Diagnostics/Diagnostic.cs @@ -82,121 +82,124 @@ public class Diagnostic public string FormatANSI() { - try + var sb = new StringBuilder(); + + sb.Append(Severity switch { - var sb = new StringBuilder(); + DiagnosticSeverity.Error => ConsoleColors.Colorize("error", ConsoleColors.Bold + ConsoleColors.Red), + DiagnosticSeverity.Warning => ConsoleColors.Colorize("warning", ConsoleColors.Bold + ConsoleColors.Yellow), + DiagnosticSeverity.Info => ConsoleColors.Colorize("info", ConsoleColors.Bold + ConsoleColors.Blue), + _ => ConsoleColors.Colorize("unknown", ConsoleColors.Bold + ConsoleColors.White) + }); - sb.Append(Severity switch - { - DiagnosticSeverity.Error => ConsoleColors.Colorize("error", ConsoleColors.Bold + ConsoleColors.Red), - DiagnosticSeverity.Warning => ConsoleColors.Colorize("warning", ConsoleColors.Bold + ConsoleColors.Yellow), - DiagnosticSeverity.Info => ConsoleColors.Colorize("info", ConsoleColors.Bold + ConsoleColors.Blue), - _ => ConsoleColors.Colorize("unknown", ConsoleColors.Bold + ConsoleColors.White) - }); + if (Span.HasValue) + { + sb.Append(ConsoleColors.Colorize($" at {Span.Value}", ConsoleColors.Faint)); + } - if (Span.HasValue) + sb.Append(": "); + sb.Append(ConsoleColors.Colorize(Message, ConsoleColors.BrightWhite)); + + if (Span.HasValue) + { + sb.AppendLine(); + var text = File.ReadAllText(Span.Value.FilePath); + + var tokenizer = new Tokenizer(Span.Value.FilePath, text); + tokenizer.Tokenize(); + + var lines = text.Split('\n'); + + var startLine = Span.Value.Start.Line; + var endLine = Span.Value.End.Line; + + const int CONTEXT_LINES = 3; + + var contextStartLine = Math.Max(1, startLine - CONTEXT_LINES); + var contextEndLine = Math.Min(lines.Length, endLine + CONTEXT_LINES); + + var numberPadding = contextEndLine.ToString().Length; + + var codePadding = 0; + for (var i = contextStartLine - 1; i < contextEndLine && i < lines.Length; i++) { - sb.Append(ConsoleColors.Colorize($" at {Span.Value}", ConsoleColors.Faint)); + var lineLength = lines[i].Length; + if (lineLength > codePadding) + { + codePadding = lineLength; + } } - sb.Append(": "); - sb.Append(ConsoleColors.Colorize(Message, ConsoleColors.BrightWhite)); + sb.Append('╭'); + sb.Append(new string('─', numberPadding + 2)); + sb.Append('┬'); + sb.Append(new string('─', codePadding + 2)); + sb.Append('╮'); + sb.AppendLine(); - if (Span.HasValue) + for (var i = contextStartLine; i <= contextEndLine; i++) { - sb.AppendLine(); - var text = File.ReadAllText(Span.Value.FilePath); + var line = lines[i - 1]; - var lines = text.Split('\n'); - - var startLine = Span.Value.Start.Line; - var endLine = Span.Value.End.Line; - - const int CONTEXT_LINES = 3; - - var contextStartLine = Math.Max(1, startLine - CONTEXT_LINES); - var contextEndLine = Math.Min(lines.Length, endLine + CONTEXT_LINES); - - var numberPadding = contextEndLine.ToString().Length; - var codePadding = lines.Skip(contextStartLine - 1).Take(contextEndLine - contextStartLine + 1).Max(x => x.Length); - - sb.Append('╭'); - sb.Append(new string('─', numberPadding + 2)); - sb.Append('┬'); - sb.Append(new string('─', codePadding + 2)); - sb.Append('╮'); + sb.Append("│ "); + sb.Append(i.ToString().PadRight(numberPadding)); + sb.Append(" │ "); + sb.Append(ApplySyntaxHighlighting(line.PadRight(codePadding), i, tokenizer.Tokens)); + // sb.Append(line.PadRight(codePadding)); + sb.Append(" │"); sb.AppendLine(); - var tokenizer = new Tokenizer(Span.Value.FilePath, text); - tokenizer.Tokenize(); - - for (var i = contextStartLine; i <= contextEndLine; i++) + if (i >= startLine && i <= endLine) { - var line = lines[i - 1]; + var markerStartColumn = 1; + var markerEndColumn = line.Length; + + if (i == startLine) + { + markerStartColumn = Span.Value.Start.Column; + } + + if (i == endLine) + { + markerEndColumn = Span.Value.End.Column; + } + + var markerLength = markerEndColumn - markerStartColumn; + var marker = new string('^', markerLength); + + var markerColor = Severity switch + { + DiagnosticSeverity.Info => ConsoleColors.Blue, + DiagnosticSeverity.Warning => ConsoleColors.Yellow, + DiagnosticSeverity.Error => ConsoleColors.Red, + _ => ConsoleColors.White + }; sb.Append("│ "); - sb.Append(i.ToString().PadRight(numberPadding)); + sb.Append(new string(' ', numberPadding)); sb.Append(" │ "); - sb.Append(ApplySyntaxHighlighting(line.PadRight(codePadding), i, tokenizer.Tokens)); + sb.Append(new string(' ', markerStartColumn - 1)); + sb.Append(ConsoleColors.Colorize(marker, markerColor)); + sb.Append(new string(' ', codePadding - (markerStartColumn - 1) - markerLength)); sb.Append(" │"); sb.AppendLine(); - - if (i >= startLine && i <= endLine) - { - var markerStartColumn = 1; - var markerEndColumn = line.Length; - - if (i == startLine) - { - markerStartColumn = Math.Min(Span.Value.Start.Column, 1); - } - - if (i == endLine) - { - markerEndColumn = Math.Min(Span.Value.End.Column, line.Length); - } - - var markerLength = markerEndColumn - markerStartColumn; - var marker = new string('^', markerLength); - - var markerColor = Severity switch - { - DiagnosticSeverity.Info => ConsoleColors.Blue, - DiagnosticSeverity.Warning => ConsoleColors.Yellow, - DiagnosticSeverity.Error => ConsoleColors.Red, - _ => ConsoleColors.White - }; - - sb.Append("│ "); - sb.Append(new string(' ', numberPadding)); - sb.Append(" │ "); - sb.Append(new string(' ', markerStartColumn - 1)); - sb.Append(ConsoleColors.Colorize(marker, markerColor)); - sb.Append(new string(' ', codePadding - markerEndColumn + 1)); - sb.Append(" │"); - sb.AppendLine(); - } } - - sb.Append('╰'); - sb.Append(new string('─', numberPadding + 2)); - sb.Append('┴'); - sb.Append(new string('─', codePadding + 2)); - sb.Append('╯'); } - if (Help != null) - { - sb.AppendLine(); - sb.Append(ConsoleColors.Colorize($"help: {Help}", ConsoleColors.Cyan)); - } - - return sb.ToString(); + sb.Append('╰'); + sb.Append(new string('─', numberPadding + 2)); + sb.Append('┴'); + sb.Append(new string('─', codePadding + 2)); + sb.Append('╯'); } - catch (Exception) + + if (Help != null) { - return ConsoleColors.Colorize("Failed to generate error message", ConsoleColors.Red); + sb.AppendLine(); + sb.Append(ConsoleColors.Colorize($"help: {Help}", ConsoleColors.Cyan)); } + + return sb.ToString(); } private static string ApplySyntaxHighlighting(string line, int lineNumber, List tokens) @@ -219,25 +222,34 @@ public class Diagnostic var tokenStart = token.Span.Start.Column; var tokenEnd = token.Span.End.Column; - if (tokenStart > currentColumn) + if (tokenStart > currentColumn && currentColumn - 1 < line.Length) { - var beforeToken = line.Substring(currentColumn - 1, tokenStart - currentColumn); - sb.Append(beforeToken); + var beforeLength = Math.Min(tokenStart - currentColumn, line.Length - (currentColumn - 1)); + if (beforeLength > 0) + { + var beforeToken = line.Substring(currentColumn - 1, beforeLength); + sb.Append(beforeToken); + } } var tokenLength = tokenEnd - tokenStart; - if (tokenStart - 1 + tokenLength <= line.Length) + if (tokenStart >= 1 && tokenStart - 1 < line.Length && tokenLength > 0) { - var tokenText = line.Substring(tokenStart - 1, tokenLength); + var availableLength = line.Length - (tokenStart - 1); + var actualLength = Math.Min(tokenLength, availableLength); - var coloredToken = ColorizeToken(token, tokenText); - sb.Append(coloredToken); + if (actualLength > 0) + { + var tokenText = line.Substring(tokenStart - 1, actualLength); + var coloredToken = ColorizeToken(token, tokenText); + sb.Append(coloredToken); + } } currentColumn = tokenEnd; } - if (currentColumn <= line.Length) + if (currentColumn - 1 < line.Length) { var remaining = line[(currentColumn - 1)..]; sb.Append(remaining); diff --git a/compiler/NubLang/Syntax/Tokenizer.cs b/compiler/NubLang/Syntax/Tokenizer.cs index 2391e3a..fcd7786 100644 --- a/compiler/NubLang/Syntax/Tokenizer.cs +++ b/compiler/NubLang/Syntax/Tokenizer.cs @@ -90,23 +90,23 @@ public sealed class Tokenizer { try { - // Skip whitespace and increment line counter if newline var current = Peek()!.Value; if (char.IsWhiteSpace(current)) { if (current is '\n') { _line += 1; - _column = 1; + // note(nub31): Next increments the column, so 0 is correct here + _column = 0; } Next(); continue; } - // Skip single line comments but keep newline so next iteration increments the line counter if (current == '/' && Peek(1) == '/') { + // note(nub31): Keep newline so next iteration increments the line counter while (Peek() is not '\n') { Next(); diff --git a/examples/raylib/main.nub b/examples/raylib/main.nub index 782e6a4..23d367a 100644 --- a/examples/raylib/main.nub +++ b/examples/raylib/main.nub @@ -4,7 +4,7 @@ module "main" extern "main" func main(args: []cstring): i64 { - raylib::SetConfigFlags(4 | 64) + raylib: :SetConfigFlags(4 | 64) raylib::InitWindow(1600, 900, "Hi from nub-lang") defer raylib::CloseWindow()