mirror of
https://github.com/QL-Win/QuickLook.git
synced 2026-05-08 03:06:29 +08:00
Add .jsonl syntax highlighting support to TextViewer (#1906)
This commit is contained in:
@@ -163,9 +163,9 @@ public partial class TextViewerPanel : TextEditor, IDisposable
|
||||
// RTL: Ctrl + RShift
|
||||
// LTR: Ctrl + LShift
|
||||
if (Keyboard.IsKeyDown(Key.RightShift))
|
||||
FlowDirection = System.Windows.FlowDirection.RightToLeft;
|
||||
FlowDirection = FlowDirection.RightToLeft;
|
||||
else if (Keyboard.IsKeyDown(Key.LeftShift))
|
||||
FlowDirection = System.Windows.FlowDirection.LeftToRight;
|
||||
FlowDirection = FlowDirection.LeftToRight;
|
||||
}
|
||||
else if (Keyboard.IsKeyDown(Key.LeftAlt) || Keyboard.IsKeyDown(Key.RightAlt))
|
||||
{
|
||||
|
||||
+47
@@ -0,0 +1,47 @@
|
||||
// Copyright © 2017-2026 QL-Win Contributors
|
||||
//
|
||||
// This file is part of QuickLook program.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
using ICSharpCode.AvalonEdit.Highlighting;
|
||||
using ICSharpCode.AvalonEdit.Rendering;
|
||||
using QuickLook.Plugin.TextViewer.Themes.HighlightingDefinitions.Light;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuickLook.Plugin.TextViewer.Themes.HighlightingDefinitions.Dark;
|
||||
|
||||
public class JSONLHighlightingDefinition : DarkHighlightingDefinition
|
||||
{
|
||||
public override string Name => "JSONL";
|
||||
|
||||
public override string Extension => ".jsonl";
|
||||
|
||||
public override HighlightingRuleSet MainRuleSet => new();
|
||||
|
||||
public override HighlightingColor GetNamedColor(string name) => null;
|
||||
|
||||
public override IEnumerable<HighlightingColor> NamedHighlightingColors => [];
|
||||
|
||||
public override DocumentColorizingTransformer[] LineTransformers { get; } =
|
||||
[
|
||||
new JsonLineHighlighter(
|
||||
keyColor: "#9CDCF0", // field names (VSCode default)
|
||||
stringColor: "#CE9178", // string values (VSCode default)
|
||||
numberColor: "#B5CEA8", // numbers (VSCode default)
|
||||
boolNullColor:"#569CD6", // true/false/null (VSCode default)
|
||||
braceColor: "#DA66BE", // { }
|
||||
bracketColor: "#FFD710") // [ ]
|
||||
];
|
||||
}
|
||||
+211
@@ -0,0 +1,211 @@
|
||||
// Copyright © 2017-2026 QL-Win Contributors
|
||||
//
|
||||
// This file is part of QuickLook program.
|
||||
//
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, either version 3 of the License, or
|
||||
// (at your option) any later version.
|
||||
//
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License for more details.
|
||||
//
|
||||
// You should have received a copy of the GNU General Public License
|
||||
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
using ICSharpCode.AvalonEdit.Document;
|
||||
using ICSharpCode.AvalonEdit.Highlighting;
|
||||
using ICSharpCode.AvalonEdit.Rendering;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace QuickLook.Plugin.TextViewer.Themes.HighlightingDefinitions.Light;
|
||||
|
||||
public class JSONLHighlightingDefinition : LightHighlightingDefinition
|
||||
{
|
||||
public override string Name => "JSONL";
|
||||
|
||||
public override string Extension => ".jsonl";
|
||||
|
||||
public override HighlightingRuleSet MainRuleSet => new();
|
||||
|
||||
public override HighlightingColor GetNamedColor(string name) => null;
|
||||
|
||||
public override IEnumerable<HighlightingColor> NamedHighlightingColors => [];
|
||||
|
||||
public override DocumentColorizingTransformer[] LineTransformers { get; } =
|
||||
[
|
||||
new JsonLineHighlighter(
|
||||
keyColor: "#0451A5", // field names (VSCode light default)
|
||||
stringColor: "#A31515", // string values (VSCode light default)
|
||||
numberColor: "#098658", // numbers (VSCode light default)
|
||||
boolNullColor:"#0000FF", // true/false/null (VSCode light default)
|
||||
braceColor: "#800000", // { }
|
||||
bracketColor: "#0451A5") // [ ]
|
||||
];
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Per-line JSON tokenizer for JSONL (JSON Lines) files.
|
||||
/// Each call to ColorizeLine processes one independent JSON object/array.
|
||||
/// </summary>
|
||||
public class JsonLineHighlighter(
|
||||
string keyColor,
|
||||
string stringColor,
|
||||
string numberColor,
|
||||
string boolNullColor,
|
||||
string braceColor,
|
||||
string bracketColor) : DocumentColorizingTransformer
|
||||
{
|
||||
protected override void ColorizeLine(DocumentLine line)
|
||||
{
|
||||
var text = CurrentContext.Document.GetText(line);
|
||||
if (string.IsNullOrWhiteSpace(text))
|
||||
return;
|
||||
|
||||
ColorizeJsonLine(line, text);
|
||||
}
|
||||
|
||||
private void ColorizeJsonLine(DocumentLine line, string text)
|
||||
{
|
||||
// Stack: true = currently inside an object (next unquoted string is a key),
|
||||
// false = currently inside an array (all strings are values).
|
||||
var contextStack = new Stack<bool>();
|
||||
bool expectKey = false;
|
||||
|
||||
int i = 0;
|
||||
while (i < text.Length)
|
||||
{
|
||||
char c = text[i];
|
||||
|
||||
if (char.IsWhiteSpace(c)) { i++; continue; }
|
||||
|
||||
switch (c)
|
||||
{
|
||||
case '{':
|
||||
Colorize(line, i, i + 1, braceColor);
|
||||
contextStack.Push(true);
|
||||
expectKey = true;
|
||||
i++;
|
||||
break;
|
||||
|
||||
case '}':
|
||||
Colorize(line, i, i + 1, braceColor);
|
||||
if (contextStack.Count > 0) contextStack.Pop();
|
||||
expectKey = false;
|
||||
i++;
|
||||
break;
|
||||
|
||||
case '[':
|
||||
Colorize(line, i, i + 1, bracketColor);
|
||||
contextStack.Push(false);
|
||||
expectKey = false;
|
||||
i++;
|
||||
break;
|
||||
|
||||
case ']':
|
||||
Colorize(line, i, i + 1, bracketColor);
|
||||
if (contextStack.Count > 0) contextStack.Pop();
|
||||
expectKey = false;
|
||||
i++;
|
||||
break;
|
||||
|
||||
case ':':
|
||||
// Colon separates key from value; next token is a value.
|
||||
expectKey = false;
|
||||
i++;
|
||||
break;
|
||||
|
||||
case ',':
|
||||
// In object context the next string is a key; in array it is a value.
|
||||
expectKey = contextStack.Count > 0 && contextStack.Peek();
|
||||
i++;
|
||||
break;
|
||||
|
||||
case '"':
|
||||
case '\'':
|
||||
i = TokenizeString(line, text, i, c, expectKey);
|
||||
if (expectKey)
|
||||
expectKey = false; // key consumed, colon follows next
|
||||
break;
|
||||
|
||||
default:
|
||||
if (c == '-' || char.IsDigit(c))
|
||||
{
|
||||
i = TokenizeNumber(line, text, i);
|
||||
expectKey = false;
|
||||
}
|
||||
else if (char.IsLetter(c))
|
||||
{
|
||||
i = TokenizeKeyword(line, text, i);
|
||||
expectKey = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
i++;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private int TokenizeString(DocumentLine line, string text, int start, char quote, bool isKey)
|
||||
{
|
||||
int i = start + 1; // skip opening quote
|
||||
while (i < text.Length)
|
||||
{
|
||||
if (text[i] == '\\')
|
||||
{
|
||||
i += 2; // skip escape sequence
|
||||
continue;
|
||||
}
|
||||
if (text[i] == quote)
|
||||
{
|
||||
i++; // include closing quote
|
||||
break;
|
||||
}
|
||||
i++;
|
||||
}
|
||||
Colorize(line, start, i, isKey ? keyColor : stringColor);
|
||||
return i;
|
||||
}
|
||||
|
||||
private int TokenizeNumber(DocumentLine line, string text, int start)
|
||||
{
|
||||
int i = start;
|
||||
if (i < text.Length && text[i] == '-') i++; // optional leading minus
|
||||
while (i < text.Length && char.IsDigit(text[i])) i++;
|
||||
if (i < text.Length && text[i] == '.')
|
||||
{
|
||||
i++;
|
||||
while (i < text.Length && char.IsDigit(text[i])) i++;
|
||||
}
|
||||
if (i < text.Length && (text[i] == 'e' || text[i] == 'E'))
|
||||
{
|
||||
i++;
|
||||
if (i < text.Length && (text[i] == '+' || text[i] == '-')) i++;
|
||||
while (i < text.Length && char.IsDigit(text[i])) i++;
|
||||
}
|
||||
Colorize(line, start, i, numberColor);
|
||||
return i;
|
||||
}
|
||||
|
||||
private int TokenizeKeyword(DocumentLine line, string text, int start)
|
||||
{
|
||||
int i = start;
|
||||
while (i < text.Length && char.IsLetter(text[i])) i++;
|
||||
string keyword = text.Substring(start, i - start);
|
||||
if (keyword is "true" or "false" or "null")
|
||||
Colorize(line, start, i, boolNullColor);
|
||||
return i;
|
||||
}
|
||||
|
||||
private void Colorize(DocumentLine line, int from, int to, string hexColor)
|
||||
{
|
||||
ChangeLinePart(line.Offset + from, line.Offset + to, el =>
|
||||
{
|
||||
el.TextRunProperties.SetForegroundBrush(hexColor.ToBrush());
|
||||
});
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user