aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--package.json3
-rw-r--r--src/autocomplete.ts66
-rw-r--r--src/highlight.ts61
-rw-r--r--src/index.ts76
-rw-r--r--src/syntax.grammar53
5 files changed, 196 insertions, 63 deletions
diff --git a/package.json b/package.json
index bfd6934..309047f 100644
--- a/package.json
+++ b/package.json
@@ -1,6 +1,6 @@
{
"name": "@emprespresso/codemirror-lang-tabloid",
- "version": "0.1.0",
+ "version": "0.2.0",
"description": "tabloid language support for CodeMirror",
"scripts": {
"test": "mocha test/test.js",
@@ -17,6 +17,7 @@
"types": "dist/index.d.ts",
"sideEffects": false,
"dependencies": {
+ "@codemirror/autocomplete": "^6.20.0",
"@codemirror/language": "^6.0.0",
"@lezer/highlight": "^1.0.0",
"@lezer/lr": "^1.0.0"
diff --git a/src/autocomplete.ts b/src/autocomplete.ts
new file mode 100644
index 0000000..6f894ad
--- /dev/null
+++ b/src/autocomplete.ts
@@ -0,0 +1,66 @@
+import {
+ CompletionContext,
+ CompletionResult,
+ Completion,
+} from "@codemirror/autocomplete";
+
+const keywords: Completion[] = [
+ // Function and variable declarations
+ { label: "DISCOVER HOW TO", type: "keyword", info: "Function declaration" },
+ { label: "WITH", type: "keyword", info: "Function parameters" },
+ { label: "EXPERTS CLAIM", type: "keyword", info: "Variable assignment" },
+ { label: "TO BE", type: "keyword", info: "Assignment operator" },
+
+ // Control flow
+ { label: "WHAT IF", type: "keyword", info: "If statement" },
+ { label: "LIES!", type: "keyword", info: "Else statement" },
+ { label: "SHOCKING DEVELOPMENT", type: "keyword", info: "Return statement" },
+
+ // Blocks
+ { label: "RUMOR HAS IT", type: "keyword", info: "Block start" },
+ { label: "END OF STORY", type: "keyword", info: "Block end" },
+
+ // I/O
+ { label: "YOU WON'T WANT TO MISS", type: "keyword", info: "Print statement" },
+ { label: "LATEST NEWS ON", type: "keyword", info: "Input statement" },
+
+ // Boolean literals
+ { label: "TOTALLY RIGHT", type: "keyword", info: "True" },
+ { label: "COMPLETELY WRONG", type: "keyword", info: "False" },
+
+ // Operators
+ { label: "IS ACTUALLY", type: "keyword", info: "Equality comparison" },
+ { label: "BEATS", type: "keyword", info: "Greater than" },
+ { label: "SMALLER THAN", type: "keyword", info: "Less than" },
+ { label: "AND", type: "keyword", info: "Logical AND" },
+ { label: "OR", type: "keyword", info: "Logical OR" },
+ { label: "PLUS", type: "keyword", info: "Addition" },
+ { label: "MINUS", type: "keyword", info: "Subtraction" },
+ { label: "TIMES", type: "keyword", info: "Multiplication" },
+ { label: "DIVIDED BY", type: "keyword", info: "Division" },
+ { label: "MODULO", type: "keyword", info: "Modulus" },
+ { label: "OF", type: "keyword", info: "Function call" },
+
+ // Program end
+ {
+ label: "PLEASE LIKE AND SUBSCRIBE",
+ type: "keyword",
+ info: "End of program",
+ },
+];
+
+export function tabloidCompletion(
+ context: CompletionContext,
+): CompletionResult | null {
+ const word = context.matchBefore(/[A-Z'][A-Z\-'\s]*/);
+
+ if (!word || (word.from === word.to && !context.explicit)) {
+ return null;
+ }
+
+ return {
+ from: word.from,
+ options: keywords,
+ filter: false, // Let CodeMirror handle filtering
+ };
+}
diff --git a/src/highlight.ts b/src/highlight.ts
new file mode 100644
index 0000000..7835929
--- /dev/null
+++ b/src/highlight.ts
@@ -0,0 +1,61 @@
+import { styleTags, tags as t } from "@lezer/highlight";
+
+export const highlighting = styleTags({
+ // Comments
+ Comment: t.lineComment,
+
+ // Literals
+ NumberLiteral: t.number,
+ StringLiteral: t.string,
+ BooleanLiteral: t.bool,
+
+ // Identifiers
+ Identifier: t.variableName,
+ "FunctionDecl/Identifier": t.function(t.definition(t.variableName)),
+ "FunctionCall/Identifier": t.function(t.variableName),
+ "ArgumentList/Identifier": t.variableName,
+
+ // Keywords - Definitions (use controlKeyword for better color)
+ discoverHowTo: t.controlKeyword,
+ expertsClaim: t.keyword,
+ toBe: t.operator,
+ with: t.keyword,
+ of: t.keyword,
+
+ // Keywords - Control flow
+ whatIf: t.controlKeyword,
+ liesBang: t.controlKeyword,
+ shockingDevelopment: t.controlKeyword,
+
+ // Keywords - I/O
+ youWontWantToMiss: t.keyword,
+ latestNewsOn: t.keyword,
+
+ // Keywords - Block delimiters
+ rumorHasIt: t.brace,
+ endOfStory: t.brace,
+
+ // Keywords - Program structure
+ pleaseLikeAndSubscribe: t.moduleKeyword,
+
+ // Operators - Comparison
+ isActually: t.compareOperator,
+ beats: t.compareOperator,
+ smallerThan: t.compareOperator,
+
+ // Operators - Logical
+ and: t.logicOperator,
+ or: t.logicOperator,
+
+ // Operators - Arithmetic
+ plus: t.arithmeticOperator,
+ minus: t.arithmeticOperator,
+ times: t.arithmeticOperator,
+ dividedBy: t.arithmeticOperator,
+ modulo: t.arithmeticOperator,
+
+ // Punctuation
+ "(": t.paren,
+ ")": t.paren,
+ ",": t.separator,
+});
diff --git a/src/index.ts b/src/index.ts
index 14b4ffe..690572b 100644
--- a/src/index.ts
+++ b/src/index.ts
@@ -1,51 +1,55 @@
-import {parser} from "./syntax.grammar"
-import {LRLanguage, LanguageSupport, indentNodeProp, foldNodeProp, foldInside, delimitedIndent} from "@codemirror/language"
-import {styleTags, tags as t} from "@lezer/highlight"
+import { parser } from "./syntax.grammar";
+import {
+ LRLanguage,
+ LanguageSupport,
+ indentNodeProp,
+ foldNodeProp,
+ foldInside,
+ delimitedIndent,
+} from "@codemirror/language";
+import { highlighting } from "./highlight";
+import { tabloidCompletion } from "./autocomplete";
+import { autocompletion } from "@codemirror/autocomplete";
+
+// Wrap parser to debug
+const originalParse = parser.parse.bind(parser);
+parser.parse = function (input, fragments, ranges) {
+ console.log(
+ "[PARSER] parse called with input:",
+ typeof input === "string" ? input.substring(0, 100) : input,
+ );
+ const result = originalParse(input, fragments, ranges);
+ console.log("[PARSER] parse result:", result.toString());
+ return result;
+};
export const tabloidLanguage = LRLanguage.define({
parser: parser.configure({
props: [
indentNodeProp.add({
- Block: delimitedIndent({closing: "endOfStory", align: false}),
- ParenBlock: delimitedIndent({closing: ")", align: false}),
- FunctionDecl: context => context.baseIndent + context.unit,
- IfStatement: context => context.baseIndent + context.unit,
+ Block: delimitedIndent({ closing: "endOfStory", align: false }),
+ ParenBlock: delimitedIndent({ closing: ")", align: false }),
+ FunctionDecl: (context) => context.baseIndent + context.unit,
+ IfStatement: (context) => context.baseIndent + context.unit,
}),
foldNodeProp.add({
Block: foldInside,
ParenBlock: foldInside,
}),
- styleTags({
- Identifier: t.variableName,
- NumberLiteral: t.number,
- StringLiteral: t.string,
- BooleanLiteral: t.bool,
- FunctionCall: t.function(t.variableName),
- discoverHowTo: t.definitionKeyword,
- expertsClaim: t.definitionKeyword,
- toBe: t.operatorKeyword,
- rumorHasIt: t.keyword,
- endOfStory: t.keyword,
- whatIf: t.controlKeyword,
- liesBang: t.controlKeyword,
- shockingDevelopment: t.controlKeyword,
- youWontWantToMiss: t.keyword,
- latestNewsOn: t.keyword,
- pleaseLikeAndSubscribe: t.keyword,
- "with of": t.keyword,
- "isActually beats smallerThan": t.compareOperator,
- "and or": t.logicOperator,
- "plus minus times dividedBy modulo": t.arithmeticOperator,
- "( )": t.paren,
- ",": t.separator
- })
- ]
+ highlighting,
+ ],
}),
languageData: {
- commentTokens: {}
- }
-})
+ commentTokens: {},
+ closeBrackets: { brackets: ["(", '"', "'"] },
+ autocomplete: tabloidCompletion,
+ },
+});
export function tabloid() {
- return new LanguageSupport(tabloidLanguage)
+ return new LanguageSupport(tabloidLanguage, [
+ autocompletion({ override: [tabloidCompletion] }),
+ ]);
}
+
+export { tabloidCompletion };
diff --git a/src/syntax.grammar b/src/syntax.grammar
index bffd9c4..ce7ba38 100644
--- a/src/syntax.grammar
+++ b/src/syntax.grammar
@@ -85,17 +85,18 @@ ProgramEnd {
pleaseLikeAndSubscribe
}
-@skip { space | newline }
+@skip { space | newline | Comment }
@tokens {
space { $[ \t]+ }
newline { $[\n\r] }
+ Comment { "#" (![\n\r] )* }
identifierChar { $[A-Z_\-] | $[0-9] | "'" }
word { ($[A-Z_\-] | "'") identifierChar* }
- @precedence { StringLiteral, NumberLiteral, BooleanLiteral, discoverHowTo, expertsClaim, latestNewsOn, pleaseLikeAndSubscribe, rumorHasIt, shockingDevelopment, whatIf, youWontWantToMiss, toBe, with, of, endOfStory, liesBang, isActually, dividedBy, smallerThan, and, or, plus, minus, times, modulo, beats, Identifier }
+ @precedence { Comment, StringLiteral, NumberLiteral, BooleanLiteral, discoverHowTo, expertsClaim, latestNewsOn, pleaseLikeAndSubscribe, rumorHasIt, shockingDevelopment, whatIf, youWontWantToMiss, toBe, with, of, endOfStory, liesBang, isActually, dividedBy, smallerThan, and, or, plus, minus, times, modulo, beats, Identifier }
Identifier { word }
@@ -105,30 +106,30 @@ ProgramEnd {
BooleanLiteral { "TOTALLY" space "RIGHT" | "COMPLETELY" space "WRONG" }
- discoverHowTo { "DISCOVER" space "HOW" space "TO" }
- with { "WITH" }
- of { "OF" }
- expertsClaim { "EXPERTS" space "CLAIM" }
- toBe { "TO" space "BE" }
- rumorHasIt { "RUMOR" space "HAS" space "IT" }
- whatIf { "WHAT" space "IF" }
- liesBang { "LIES!" }
- endOfStory { "END" space "OF" space "STORY" }
- shockingDevelopment { "SHOCKING" space "DEVELOPMENT" }
- youWontWantToMiss { "YOU" space "WON'T" space "WANT" space "TO" space "MISS" }
- latestNewsOn { "LATEST" space "NEWS" space "ON" }
- pleaseLikeAndSubscribe { "PLEASE" space "LIKE" space "AND" space "SUBSCRIBE" }
-
- isActually { "IS" space "ACTUALLY" }
- and { "AND" }
- or { "OR" }
- plus { "PLUS" }
- minus { "MINUS" }
- times { "TIMES" }
- dividedBy { "DIVIDED" space "BY" }
- modulo { "MODULO" }
- beats { "BEATS" }
- smallerThan { "SMALLER" space "THAN" }
+ discoverHowTo[@name="discoverHowTo"] { "DISCOVER" space "HOW" space "TO" }
+ with[@name="with"] { "WITH" }
+ of[@name="of"] { "OF" }
+ expertsClaim[@name="expertsClaim"] { "EXPERTS" space "CLAIM" }
+ toBe[@name="toBe"] { "TO" space "BE" }
+ rumorHasIt[@name="rumorHasIt"] { "RUMOR" space "HAS" space "IT" }
+ whatIf[@name="whatIf"] { "WHAT" space "IF" }
+ liesBang[@name="liesBang"] { "LIES!" }
+ endOfStory[@name="endOfStory"] { "END" space "OF" space "STORY" }
+ shockingDevelopment[@name="shockingDevelopment"] { "SHOCKING" space "DEVELOPMENT" }
+ youWontWantToMiss[@name="youWontWantToMiss"] { "YOU" space "WON'T" space "WANT" space "TO" space "MISS" }
+ latestNewsOn[@name="latestNewsOn"] { "LATEST" space "NEWS" space "ON" }
+ pleaseLikeAndSubscribe[@name="pleaseLikeAndSubscribe"] { "PLEASE" space "LIKE" space "AND" space "SUBSCRIBE" }
+
+ isActually[@name="isActually"] { "IS" space "ACTUALLY" }
+ and[@name="and"] { "AND" }
+ or[@name="or"] { "OR" }
+ plus[@name="plus"] { "PLUS" }
+ minus[@name="minus"] { "MINUS" }
+ times[@name="times"] { "TIMES" }
+ dividedBy[@name="dividedBy"] { "DIVIDED" space "BY" }
+ modulo[@name="modulo"] { "MODULO" }
+ beats[@name="beats"] { "BEATS" }
+ smallerThan[@name="smallerThan"] { "SMALLER" space "THAN" }
"(" ")" ","
}