Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Package.resolved

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 4 additions & 0 deletions Public/css/highlight.css
Original file line number Diff line number Diff line change
Expand Up @@ -85,6 +85,10 @@
color: #c0c;
}

.exp-error {
background: #d22;
}

span.match-char {
background: rgba(112, 176, 224, 0.5);
color: #101112;
Expand Down
30 changes: 28 additions & 2 deletions Public/js/app.js
Original file line number Diff line number Diff line change
Expand Up @@ -227,6 +227,10 @@ export class App {

onExpressionFieldChange() {
if (!this.expressionField.value) {
this.expressionField.tokens = [];
this.expressionField.error = null;
this.dslView.value = "";
this.dslView.error = null;
this.updateMatchCount(0, "match-count");
return;
}
Expand Down Expand Up @@ -349,13 +353,35 @@ export class App {
} else {
this.expressionField.tokens = [];
}
this.expressionField.error = response.error;
if (response.error) {
try {
const error = JSON.parse(response.error);
if (error) {
this.expressionField.error = error;
}
} catch (e) {
this.expressionField.error = response.error;
}
} else {
this.expressionField.error = null;
}
break;
case "convertToDSL":
if (response.result) {
this.dslView.value = JSON.parse(response.result);
}
this.dslView.error = response.error;
if (response.error) {
try {
const error = JSON.parse(response.error);
if (error) {
this.dslView.error = error;
}
} catch (e) {
this.dslView.error = response.error;
}
} else {
this.dslView.error = null;
}
break;
case "match":
if (response.result) {
Expand Down
30 changes: 21 additions & 9 deletions Public/js/views/dsl_view.js
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ export class DSLView extends EventDispatcher {
}

set value(val) {
this.editor.setValue(val || this.defaultText);
this.editor.setValue(val);
}

set error(error) {
Expand All @@ -32,14 +32,26 @@ export class DSLView extends EventDispatcher {
if (!error) {
return;
}

widgets.push(
editor.addLineWidget(0, ErrorMessage.create(error), {
coverGutter: false,
noHScroll: true,
above: true,
})
);
if (typeof error === "string" && error instanceof String) {
widgets.push(
editor.addLineWidget(0, ErrorMessage.create(error), {
coverGutter: false,
noHScroll: true,
above: true,
})
);
} else {
for (const e of error) {
const message = ErrorMessage.create(e.message);
widgets.push(
editor.addLineWidget(0, message, {
coverGutter: false,
noHScroll: true,
above: true,
})
);
}
}
});
}

Expand Down
28 changes: 24 additions & 4 deletions Public/js/views/expression_field.js
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,34 @@ export class ExpressionField extends EventDispatcher {

set error(error) {
if (error) {
const content = Utils.htmlSafe(error);
this.errorMessageTooltip.setContent(
`<span class="fw-bolder text-danger">Parse Error:</span> ${content}`
);
let message = "";
if (typeof error === "string" && error instanceof String) {
const errorMessage = Utils.htmlSafe(error);
message = `<span class="fw-bolder text-danger">Parse Error:</span> ${errorMessage}`;
} else {
message = error
.map((e) => {
const errorMessage = Utils.htmlSafe(e.message);
return `<span class="fw-bolder text-danger">${e.behavior}:</span> ${errorMessage}`;
})
.join("<br>");
this.highlighter.drawError(error);
}
this.errorMessageTooltip.setContent(message);
document
.getElementById("expression-field-error")
.classList.remove("d-none");
} else {
this.errorMessageTooltip.setContent("");
document.getElementById("expression-field-error").classList.add("d-none");
this.highlighter.clearError();
}

tippy(".exp-error", {
allowHTML: true,
animation: false,
placement: "bottom",
});
}

init(container) {
Expand Down Expand Up @@ -85,6 +102,9 @@ export class ExpressionField extends EventDispatcher {
...tooltipProps,
onShow: (instance) => {
const index = instance.reference.dataset.tokenIndex;
if (index === undefined) {
return false;
}
const token = this.expressionTokens[index];
this.onHover(token, instance);
return false;
Expand Down
61 changes: 51 additions & 10 deletions Public/js/views/expression_highlighter.js
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ export default class ExpressionHighlighter extends EventDispatcher {
this.editor = editor;
this.activeMarks = [];
this.hoverMarks = [];
this.widgets = [];
}

draw(tokens) {
Expand Down Expand Up @@ -60,13 +61,52 @@ export default class ExpressionHighlighter extends EventDispatcher {
});
}

drawError(errors) {
this.clearError();

const pre = ExpressionHighlighter.CSS_PREFIX;
const editor = this.editor;
editor.operation(() => {
for (const error of errors) {
const location = Editor.calcRangePos(
this.editor,
error.location.start,
error.location.end - error.location.start
);
const widget = document.createElement("span");
widget.className = `${pre}-error`;

widget.style.height = `2px`;
widget.style.zIndex = "10";
widget.setAttribute("data-tippy-content", error.message);

editor.addWidget(location.startPos, widget);
const startCoords = editor.charCoords(location.startPos, "local");
const endCoords = editor.charCoords(location.endPos, "local");
widget.style.left = `${startCoords.left + 1}px`;
widget.style.top = `${startCoords.bottom}px`;
widget.style.width = `${endCoords.left - startCoords.left - 2}px`;

this.widgets.push(widget);
}
});
}

clear() {
this.editor.operation(() => {
let marks = this.activeMarks;
for (var i = 0, l = marks.length; i < l; i++) {
marks[i].clear();
for (const mark of this.activeMarks) {
mark.clear();
}
this.activeMarks.length = 0;
});
}

clearError() {
this.editor.operation(() => {
for (const widget of this.widgets) {
widget.parentNode.removeChild(widget);
}
marks.length = 0;
this.widgets.length = 0;
});
}

Expand All @@ -77,9 +117,7 @@ export default class ExpressionHighlighter extends EventDispatcher {
return;
}

while (this.hoverMarks.length) {
this.hoverMarks.pop().clear();
}
this.clearHover();

if (selection) {
this.drawBorder(selection, "selected");
Expand Down Expand Up @@ -119,9 +157,12 @@ export default class ExpressionHighlighter extends EventDispatcher {
}

clearHover() {
while (this.hoverMarks.length) {
this.hoverMarks.pop().clear();
}
this.editor.operation(() => {
for (const mark of this.hoverMarks) {
mark.clear();
}
this.hoverMarks.length = 0;
});
}
}

Expand Down
8 changes: 6 additions & 2 deletions Sources/DSLConverter/DSLConverter.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,13 @@ import Foundation
@testable @_spi(RegexBuilder) import _StringProcessing
@testable @_spi(PatternConverter) import _StringProcessing

struct DSLConverter {
class DSLConverter {
private(set) var diagnostics: Diagnostics?

func convert(_ pattern: String, matchingOptions: [String] = []) throws -> String {
let ast = try _RegexParser.parse(pattern, .traditional)
let ast = _RegexParser.parseWithRecovery(pattern, .traditional)
diagnostics = ast.diags

var builderDSL = renderAsBuilderDSL(ast: ast)
if builderDSL.last == "\n" {
builderDSL = String(builderDSL.dropLast())
Expand Down
39 changes: 38 additions & 1 deletion Sources/DSLConverter/Main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,34 @@ struct Main {

let data = try JSONEncoder().encode(builderDSL)
print(String(data: data, encoding: .utf8) ?? "")

if let diagnostics = converter.diagnostics {
let errors = diagnostics.diags.map {
let location = $0.location
let (start, end) = (location.start, location.end)

let behavior = switch $0.behavior {
case .fatalError:
"Fatal Error"
case .error:
"Error"
case .warning:
"Warning"
}
return LocatedMessage(
behavior: behavior,
message: $0.message,
location: Location(
start: start.utf16Offset(in: pattern), end: end.utf16Offset(in: pattern)
)
)
}

let data = try JSONEncoder().encode(errors)
print(String(data: data, encoding: .utf8) ?? "", to: &standardError)
}
} catch {
print("\(error)", to:&standardError)
print("\(error)", to: &standardError)
}
}
}
Expand All @@ -28,3 +54,14 @@ extension FileHandle: @retroactive TextOutputStream {
self.write(data)
}
}

struct Location: Codable {
let start: Int
let end: Int
}

struct LocatedMessage: Codable {
let behavior: String
let message: String
let location: Location
}
36 changes: 32 additions & 4 deletions Sources/ExpressionParser/Main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -12,15 +12,37 @@ struct Main {
var parser = ExpressionParser(pattern: pattern, matchingOptions: matchingOptions)
parser.parse()

let data = try JSONEncoder().encode(parser.tokens)
let encoder = JSONEncoder()
let data = try encoder.encode(parser.tokens)
print(String(data: data, encoding: .utf8) ?? "")

if let diagnostics = parser.diagnostics {
for diag in diagnostics.diags {
print("\(diag.message)", to:&standardError)
let errors = diagnostics.diags.map {
let location = $0.location
let (start, end) = (location.start, location.end)

let behavior = switch $0.behavior {
case .fatalError:
"Fatal Error"
case .error:
"Error"
case .warning:
"Warning"
}
return LocatedMessage(
behavior: behavior,
message: $0.message,
location: Location(
start: start.utf16Offset(in: pattern), end: end.utf16Offset(in: pattern)
)
)
}

let data = try JSONEncoder().encode(errors)
print(String(data: data, encoding: .utf8) ?? "", to: &standardError)
}
} catch {
print("\(error)", to:&standardError)
print("\(error)", to: &standardError)
}
}
}
Expand All @@ -33,3 +55,9 @@ extension FileHandle: @retroactive TextOutputStream {
self.write(data)
}
}

struct LocatedMessage: Codable {
let behavior: String
let message: String
let location: Location
}
2 changes: 1 addition & 1 deletion Sources/Matcher/Main.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ struct Main {
let data = try JSONEncoder().encode(matches)
print(String(data: data, encoding: .utf8) ?? "")
} catch {
print("\(error)", to:&standardError)
print("\(error)", to: &standardError)
}
}
}
Expand Down
Loading