Is there an updatet version for CodeMirror 5 (yes I know that CodeMirror 6 is the latest version) that support newer languages functions in C# (for example 10).
For example something like string interpolation is currently not correctly syntax highlighted.
It looks like the Kotlin mode (also implemented in clike.js) has support for some kind of string interpolation, but that doesn’t appear to go back to the base tokenizer inside ${}. I’m not sure the clike framework, as it is, allows for that kind of nesting magic.
I have been playing around with the string interpolation and this seems to work as I wanted (and almost the same as what Visual Studio from Microsoft does)
def("text/x-csharp", {
name: "clike",
keywords: words("abstract as async await base break case catch checked class const continue" +
" default delegate do else enum event explicit extern finally fixed for" +
" foreach goto if implicit in init interface internal is lock namespace new" +
" operator out override params private protected public readonly record ref required return sealed" +
" sizeof stackalloc static struct switch this throw try typeof unchecked" +
" unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
" global group into join let orderby partial remove select set value var yield"),
types: words("Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func" +
" Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32" +
" UInt64 bool byte char decimal double short int long object" +
" sbyte float string ushort uint ulong"),
blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
defKeywords: words("class interface namespace record struct var"),
typeFirstDefinitions: true,
atoms: words("true false null"),
hooks: {
"@": function(stream, state) {
if (stream.eat('"')) {
state.tokenize = tokenAtString;
return tokenAtString(stream, state);
}
stream.eatWhile(/[\w\$_]/);
return "meta";
},
'$': function(stream, state) {
if (stream.peek() !== '"') return false;
state.tokenize = tokenStringInterpolation(false, false);
return state.tokenize(stream, state);
}
}
});
function tokenStringInterpolation(interpolating, insideString) {
return function (stream, state) {
var ch;
while (!stream.eol()) {
ch = stream.next();
if (interpolating) {
if (ch != "(") {
stream.skipTo("}");
state.tokenize = tokenStringInterpolation(false, insideString)
return "variable";
}
}
if (ch == '"') {
if (!insideString) {
state.tokenize = tokenStringInterpolation(interpolating, true)
return "string";
}
else {
state.tokenize = null;
return "";
}
}
if (ch == "{") {
if (stream.peek() === '{') // This '{{' escapes string interpolation
stream.next();
else {
state.tokenize = tokenStringInterpolation(true, insideString);
return "";
}
}
if (ch == "}") {
if (stream.peek() === '}') // This '}}' escapes string interpolation
stream.next();
else {
state.tokenize = tokenStringInterpolation(false, insideString);
return "";
}
}
if (ch == "(" && interpolating) {
state.tokenize = null;
return "";
}
state.tokenize = tokenStringInterpolation(false, insideString)
return "string";
}
return "string"
}
}
The only thing what I can’t get right is this char … meaby you have an idea and if you have some tips to improve the code then I’m happy to hear them because this is the first time I made something like this for codemirror.
I got string interpolation working perfectly in codemirror 5 but I had to use an external variable to keep track of the nesting level inside an interpolation string? It looks kind of ugly this way. Is there a better way to do this? … for the rest it works perfectly even with nesting inside nesting inside nesting.
If you want to put this inside codemirror 5 then feel free to use it
// CodeMirror, copyright (c) by Marijn Haverbeke and others
// Distributed under an MIT license: https://codemirror.net/5/LICENSE
var interpolatingLevel = 0;
…
def("text/x-csharp", {
name: "clike",
keywords: words("abstract as async await base break case catch checked class const continue" +
" default delegate do else enum event explicit extern finally fixed for" +
" foreach goto if implicit in init interface internal is lock namespace new" +
" operator out override params private protected public readonly record ref required return sealed" +
" sizeof stackalloc static struct switch this throw try typeof unchecked" +
" unsafe using virtual void volatile while add alias ascending descending dynamic from get" +
" global group into join let orderby partial remove select set value var yield"),
types: words("Action Boolean Byte Char DateTime DateTimeOffset Decimal Double Func" +
" Guid Int16 Int32 Int64 Object SByte Single String Task TimeSpan UInt16 UInt32" +
" UInt64 bool byte char decimal double short int long object" +
" sbyte float string ushort uint ulong"),
blockKeywords: words("catch class do else finally for foreach if struct switch try while"),
defKeywords: words("class interface namespace record struct var"),
typeFirstDefinitions: true,
atoms: words("true false null"),
hooks: {
"@": function (stream, state) {
if (stream.eat('"')) {
state.tokenize = tokenAtString;
return tokenAtString(stream, state);
}
stream.eatWhile(/[\w\$_]/);
return "meta";
},
"$": function (stream, state) {
if (stream.peek() !== '"') return false;
interpolatingLevel = 0;
state.tokenize = tokenStringInterpolation(false);
return state.tokenize(stream, state);
},
"}": function (stream, state) {
if (interpolatingLevel == 0) return false;
state.tokenize = tokenStringInterpolation(true);
return null;
},
"{": function (stream, state) {
if (interpolatingLevel == 0) return false;
state.tokenize = tokenStringInterpolation(false);
return state.tokenize(stream, state);
}
}
});
function tokenStringInterpolation(insideString) {
const interpolationChars = ['(', '"', '{', '}'];
return function (stream, state) {
var ch;
while (!stream.eol()) {
// Just keep reading as long we don't have any interpolation chars
if (!interpolationChars.indexOf(stream.peek()) == -1) {
ch = stream.next();
continue;
}
ch = stream.next();
if (ch == '"') {
if (!insideString)
state.tokenize = tokenStringInterpolation(true)
else
state.tokenize = null;
return "string";
}
if (ch == "{") {
if (stream.peek() === '{') { // This '{{' escapes string interpolation
stream.next();
state.tokenize = tokenStringInterpolation(insideString)
return "string-2";
}
else {
interpolatingLevel += 1;
state.tokenize = null;
return null;
}
}
if (ch == "}") {
if (stream.peek() === '}') {// This '}}' escapes string interpolation
stream.next();
state.tokenize = tokenStringInterpolation(insideString)
return "string-2";
}
else {
interpolatingLevel -= 1;
state.tokenize = null;
return null;
}
}
state.tokenize = tokenStringInterpolation(insideString)
return "string";
}
return "string"
}
}
function tokenTripleString(stream, state) {
var escaped = false;
while (!stream.eol()) {
if (!escaped && stream.match('"""')) {
state.tokenize = null;
break;
}
escaped = stream.next() == "\\" && !escaped;
}
return "string";
}
function tokenNestedComment(depth) {
return function (stream, state) {
var ch
while (ch = stream.next()) {
if (ch == "*" && stream.eat("/")) {
if (depth == 1) {
state.tokenize = null
break
} else {
state.tokenize = tokenNestedComment(depth - 1)
return state.tokenize(stream, state)
}
} else if (ch == "/" && stream.eat("*")) {
state.tokenize = tokenNestedComment(depth + 1)
return state.tokenize(stream, state)
}
}
return "comment"
}
}