first commit
This commit is contained in:
463
codemirror/mode/erlang/erlang.js
vendored
Normal file
463
codemirror/mode/erlang/erlang.js
vendored
Normal file
@@ -0,0 +1,463 @@
|
||||
// block; "begin", "case", "fun", "if", "receive", "try": closed by "end"
|
||||
// block internal; "after", "catch", "of"
|
||||
// guard; "when", closed by "->"
|
||||
// "->" opens a clause, closed by ";" or "."
|
||||
// "<<" opens a binary, closed by ">>"
|
||||
// "," appears in arglists, lists, tuples and terminates lines of code
|
||||
// "." resets indentation to 0
|
||||
// obsolete; "cond", "let", "query"
|
||||
|
||||
CodeMirror.defineMIME("text/x-erlang", "erlang");
|
||||
|
||||
CodeMirror.defineMode("erlang", function(cmCfg) {
|
||||
|
||||
function rval(state,stream,type) {
|
||||
// distinguish between "." as terminator and record field operator
|
||||
if (type == "record") {
|
||||
state.context = "record";
|
||||
}else{
|
||||
state.context = false;
|
||||
}
|
||||
|
||||
// remember last significant bit on last line for indenting
|
||||
if (type != "whitespace" && type != "comment") {
|
||||
state.lastToken = stream.current();
|
||||
}
|
||||
// erlang -> CodeMirror tag
|
||||
switch (type) {
|
||||
case "atom": return "atom";
|
||||
case "attribute": return "attribute";
|
||||
case "builtin": return "builtin";
|
||||
case "comment": return "comment";
|
||||
case "fun": return "meta";
|
||||
case "function": return "tag";
|
||||
case "guard": return "property";
|
||||
case "keyword": return "keyword";
|
||||
case "macro": return "variable-2";
|
||||
case "number": return "number";
|
||||
case "operator": return "operator";
|
||||
case "record": return "bracket";
|
||||
case "string": return "string";
|
||||
case "type": return "def";
|
||||
case "variable": return "variable";
|
||||
case "error": return "error";
|
||||
case "separator": return null;
|
||||
case "open_paren": return null;
|
||||
case "close_paren": return null;
|
||||
default: return null;
|
||||
}
|
||||
}
|
||||
|
||||
var typeWords = [
|
||||
"-type", "-spec", "-export_type", "-opaque"];
|
||||
|
||||
var keywordWords = [
|
||||
"after","begin","catch","case","cond","end","fun","if",
|
||||
"let","of","query","receive","try","when"];
|
||||
|
||||
var separatorWords = [
|
||||
"->",";",":",".",","];
|
||||
|
||||
var operatorWords = [
|
||||
"and","andalso","band","bnot","bor","bsl","bsr","bxor",
|
||||
"div","not","or","orelse","rem","xor"];
|
||||
|
||||
var symbolWords = [
|
||||
"+","-","*","/",">",">=","<","=<","=:=","==","=/=","/=","||","<-"];
|
||||
|
||||
var openParenWords = [
|
||||
"<<","(","[","{"];
|
||||
|
||||
var closeParenWords = [
|
||||
"}","]",")",">>"];
|
||||
|
||||
var guardWords = [
|
||||
"is_atom","is_binary","is_bitstring","is_boolean","is_float",
|
||||
"is_function","is_integer","is_list","is_number","is_pid",
|
||||
"is_port","is_record","is_reference","is_tuple",
|
||||
"atom","binary","bitstring","boolean","function","integer","list",
|
||||
"number","pid","port","record","reference","tuple"];
|
||||
|
||||
var bifWords = [
|
||||
"abs","adler32","adler32_combine","alive","apply","atom_to_binary",
|
||||
"atom_to_list","binary_to_atom","binary_to_existing_atom",
|
||||
"binary_to_list","binary_to_term","bit_size","bitstring_to_list",
|
||||
"byte_size","check_process_code","contact_binary","crc32",
|
||||
"crc32_combine","date","decode_packet","delete_module",
|
||||
"disconnect_node","element","erase","exit","float","float_to_list",
|
||||
"garbage_collect","get","get_keys","group_leader","halt","hd",
|
||||
"integer_to_list","internal_bif","iolist_size","iolist_to_binary",
|
||||
"is_alive","is_atom","is_binary","is_bitstring","is_boolean",
|
||||
"is_float","is_function","is_integer","is_list","is_number","is_pid",
|
||||
"is_port","is_process_alive","is_record","is_reference","is_tuple",
|
||||
"length","link","list_to_atom","list_to_binary","list_to_bitstring",
|
||||
"list_to_existing_atom","list_to_float","list_to_integer",
|
||||
"list_to_pid","list_to_tuple","load_module","make_ref","module_loaded",
|
||||
"monitor_node","node","node_link","node_unlink","nodes","notalive",
|
||||
"now","open_port","pid_to_list","port_close","port_command",
|
||||
"port_connect","port_control","pre_loaded","process_flag",
|
||||
"process_info","processes","purge_module","put","register",
|
||||
"registered","round","self","setelement","size","spawn","spawn_link",
|
||||
"spawn_monitor","spawn_opt","split_binary","statistics",
|
||||
"term_to_binary","time","throw","tl","trunc","tuple_size",
|
||||
"tuple_to_list","unlink","unregister","whereis"];
|
||||
|
||||
// ignored for indenting purposes
|
||||
var ignoreWords = [
|
||||
",", ":", "catch", "after", "of", "cond", "let", "query"];
|
||||
|
||||
|
||||
var smallRE = /[a-z_]/;
|
||||
var largeRE = /[A-Z_]/;
|
||||
var digitRE = /[0-9]/;
|
||||
var octitRE = /[0-7]/;
|
||||
var anumRE = /[a-z_A-Z0-9]/;
|
||||
var symbolRE = /[\+\-\*\/<>=\|:]/;
|
||||
var openParenRE = /[<\(\[\{]/;
|
||||
var closeParenRE = /[>\)\]\}]/;
|
||||
var sepRE = /[\->\.,:;]/;
|
||||
|
||||
function isMember(element,list) {
|
||||
return (-1 < list.indexOf(element));
|
||||
}
|
||||
|
||||
function isPrev(stream,string) {
|
||||
var start = stream.start;
|
||||
var len = string.length;
|
||||
if (len <= start) {
|
||||
var word = stream.string.slice(start-len,start);
|
||||
return word == string;
|
||||
}else{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
function tokenize(stream, state) {
|
||||
if (stream.eatSpace()) {
|
||||
return rval(state,stream,"whitespace");
|
||||
}
|
||||
|
||||
// attributes and type specs
|
||||
if ((peekToken(state).token == "" || peekToken(state).token == ".") &&
|
||||
stream.peek() == '-') {
|
||||
stream.next();
|
||||
if (stream.eat(smallRE) && stream.eatWhile(anumRE)) {
|
||||
if (isMember(stream.current(),typeWords)) {
|
||||
return rval(state,stream,"type");
|
||||
}else{
|
||||
return rval(state,stream,"attribute");
|
||||
}
|
||||
}
|
||||
stream.backUp(1);
|
||||
}
|
||||
|
||||
var ch = stream.next();
|
||||
|
||||
// comment
|
||||
if (ch == '%') {
|
||||
stream.skipToEnd();
|
||||
return rval(state,stream,"comment");
|
||||
}
|
||||
|
||||
// macro
|
||||
if (ch == '?') {
|
||||
stream.eatWhile(anumRE);
|
||||
return rval(state,stream,"macro");
|
||||
}
|
||||
|
||||
// record
|
||||
if ( ch == "#") {
|
||||
stream.eatWhile(anumRE);
|
||||
return rval(state,stream,"record");
|
||||
}
|
||||
|
||||
// char
|
||||
if ( ch == "$") {
|
||||
if (stream.next() == "\\") {
|
||||
if (!stream.eatWhile(octitRE)) {
|
||||
stream.next();
|
||||
}
|
||||
}
|
||||
return rval(state,stream,"string");
|
||||
}
|
||||
|
||||
// quoted atom
|
||||
if (ch == '\'') {
|
||||
if (singleQuote(stream)) {
|
||||
return rval(state,stream,"atom");
|
||||
}else{
|
||||
return rval(state,stream,"error");
|
||||
}
|
||||
}
|
||||
|
||||
// string
|
||||
if (ch == '"') {
|
||||
if (doubleQuote(stream)) {
|
||||
return rval(state,stream,"string");
|
||||
}else{
|
||||
return rval(state,stream,"error");
|
||||
}
|
||||
}
|
||||
|
||||
// variable
|
||||
if (largeRE.test(ch)) {
|
||||
stream.eatWhile(anumRE);
|
||||
return rval(state,stream,"variable");
|
||||
}
|
||||
|
||||
// atom/keyword/BIF/function
|
||||
if (smallRE.test(ch)) {
|
||||
stream.eatWhile(anumRE);
|
||||
|
||||
if (stream.peek() == "/") {
|
||||
stream.next();
|
||||
if (stream.eatWhile(digitRE)) {
|
||||
return rval(state,stream,"fun"); // f/0 style fun
|
||||
}else{
|
||||
stream.backUp(1);
|
||||
return rval(state,stream,"atom");
|
||||
}
|
||||
}
|
||||
|
||||
var w = stream.current();
|
||||
|
||||
if (isMember(w,keywordWords)) {
|
||||
pushToken(state,stream);
|
||||
return rval(state,stream,"keyword");
|
||||
}
|
||||
if (stream.peek() == "(") {
|
||||
// 'put' and 'erlang:put' are bifs, 'foo:put' is not
|
||||
if (isMember(w,bifWords) &&
|
||||
(!isPrev(stream,":") || isPrev(stream,"erlang:"))) {
|
||||
return rval(state,stream,"builtin");
|
||||
}else{
|
||||
return rval(state,stream,"function");
|
||||
}
|
||||
}
|
||||
if (isMember(w,guardWords)) {
|
||||
return rval(state,stream,"guard");
|
||||
}
|
||||
if (isMember(w,operatorWords)) {
|
||||
return rval(state,stream,"operator");
|
||||
}
|
||||
if (stream.peek() == ":") {
|
||||
if (w == "erlang") {
|
||||
return rval(state,stream,"builtin");
|
||||
} else {
|
||||
return rval(state,stream,"function");
|
||||
}
|
||||
}
|
||||
return rval(state,stream,"atom");
|
||||
}
|
||||
|
||||
// number
|
||||
if (digitRE.test(ch)) {
|
||||
stream.eatWhile(digitRE);
|
||||
if (stream.eat('#')) {
|
||||
stream.eatWhile(digitRE); // 16#10 style integer
|
||||
} else {
|
||||
if (stream.eat('.')) { // float
|
||||
stream.eatWhile(digitRE);
|
||||
}
|
||||
if (stream.eat(/[eE]/)) {
|
||||
stream.eat(/[-+]/); // float with exponent
|
||||
stream.eatWhile(digitRE);
|
||||
}
|
||||
}
|
||||
return rval(state,stream,"number"); // normal integer
|
||||
}
|
||||
|
||||
// open parens
|
||||
if (nongreedy(stream,openParenRE,openParenWords)) {
|
||||
pushToken(state,stream);
|
||||
return rval(state,stream,"open_paren");
|
||||
}
|
||||
|
||||
// close parens
|
||||
if (nongreedy(stream,closeParenRE,closeParenWords)) {
|
||||
pushToken(state,stream);
|
||||
return rval(state,stream,"close_paren");
|
||||
}
|
||||
|
||||
// separators
|
||||
if (greedy(stream,sepRE,separatorWords)) {
|
||||
// distinguish between "." as terminator and record field operator
|
||||
if (state.context == false) {
|
||||
pushToken(state,stream);
|
||||
}
|
||||
return rval(state,stream,"separator");
|
||||
}
|
||||
|
||||
// operators
|
||||
if (greedy(stream,symbolRE,symbolWords)) {
|
||||
return rval(state,stream,"operator");
|
||||
}
|
||||
|
||||
return rval(state,stream,null);
|
||||
}
|
||||
|
||||
function nongreedy(stream,re,words) {
|
||||
if (stream.current().length == 1 && re.test(stream.current())) {
|
||||
stream.backUp(1);
|
||||
while (re.test(stream.peek())) {
|
||||
stream.next();
|
||||
if (isMember(stream.current(),words)) {
|
||||
return true;
|
||||
}
|
||||
}
|
||||
stream.backUp(stream.current().length-1);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function greedy(stream,re,words) {
|
||||
if (stream.current().length == 1 && re.test(stream.current())) {
|
||||
while (re.test(stream.peek())) {
|
||||
stream.next();
|
||||
}
|
||||
while (0 < stream.current().length) {
|
||||
if (isMember(stream.current(),words)) {
|
||||
return true;
|
||||
}else{
|
||||
stream.backUp(1);
|
||||
}
|
||||
}
|
||||
stream.next();
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function doubleQuote(stream) {
|
||||
return quote(stream, '"', '\\');
|
||||
}
|
||||
|
||||
function singleQuote(stream) {
|
||||
return quote(stream,'\'','\\');
|
||||
}
|
||||
|
||||
function quote(stream,quoteChar,escapeChar) {
|
||||
while (!stream.eol()) {
|
||||
var ch = stream.next();
|
||||
if (ch == quoteChar) {
|
||||
return true;
|
||||
}else if (ch == escapeChar) {
|
||||
stream.next();
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
function Token(stream) {
|
||||
this.token = stream ? stream.current() : "";
|
||||
this.column = stream ? stream.column() : 0;
|
||||
this.indent = stream ? stream.indentation() : 0;
|
||||
}
|
||||
|
||||
function myIndent(state,textAfter) {
|
||||
var indent = cmCfg.indentUnit;
|
||||
var outdentWords = ["after","catch"];
|
||||
var token = (peekToken(state)).token;
|
||||
var wordAfter = takewhile(textAfter,/[^a-z]/);
|
||||
|
||||
if (isMember(token,openParenWords)) {
|
||||
return (peekToken(state)).column+token.length;
|
||||
}else if (token == "." || token == ""){
|
||||
return 0;
|
||||
}else if (token == "->") {
|
||||
if (wordAfter == "end") {
|
||||
return peekToken(state,2).column;
|
||||
}else if (peekToken(state,2).token == "fun") {
|
||||
return peekToken(state,2).column+indent;
|
||||
}else{
|
||||
return (peekToken(state)).indent+indent;
|
||||
}
|
||||
}else if (isMember(wordAfter,outdentWords)) {
|
||||
return (peekToken(state)).indent;
|
||||
}else{
|
||||
return (peekToken(state)).column+indent;
|
||||
}
|
||||
}
|
||||
|
||||
function takewhile(str,re) {
|
||||
var m = str.match(re);
|
||||
return m ? str.slice(0,m.index) : str;
|
||||
}
|
||||
|
||||
function popToken(state) {
|
||||
return state.tokenStack.pop();
|
||||
}
|
||||
|
||||
function peekToken(state,depth) {
|
||||
var len = state.tokenStack.length;
|
||||
var dep = (depth ? depth : 1);
|
||||
if (len < dep) {
|
||||
return new Token;
|
||||
}else{
|
||||
return state.tokenStack[len-dep];
|
||||
}
|
||||
}
|
||||
|
||||
function pushToken(state,stream) {
|
||||
var token = stream.current();
|
||||
var prev_token = peekToken(state).token;
|
||||
if (isMember(token,ignoreWords)) {
|
||||
return false;
|
||||
}else if (drop_both(prev_token,token)) {
|
||||
popToken(state);
|
||||
return false;
|
||||
}else if (drop_first(prev_token,token)) {
|
||||
popToken(state);
|
||||
return pushToken(state,stream);
|
||||
}else{
|
||||
state.tokenStack.push(new Token(stream));
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
function drop_first(open, close) {
|
||||
switch (open+" "+close) {
|
||||
case "when ->": return true;
|
||||
case "-> end": return true;
|
||||
case "-> .": return true;
|
||||
case ". .": return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
function drop_both(open, close) {
|
||||
switch (open+" "+close) {
|
||||
case "( )": return true;
|
||||
case "[ ]": return true;
|
||||
case "{ }": return true;
|
||||
case "<< >>": return true;
|
||||
case "begin end": return true;
|
||||
case "case end": return true;
|
||||
case "fun end": return true;
|
||||
case "if end": return true;
|
||||
case "receive end": return true;
|
||||
case "try end": return true;
|
||||
case "-> ;": return true;
|
||||
default: return false;
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
startState:
|
||||
function() {
|
||||
return {tokenStack: [],
|
||||
context: false,
|
||||
lastToken: null};
|
||||
},
|
||||
|
||||
token:
|
||||
function(stream, state) {
|
||||
return tokenize(stream, state);
|
||||
},
|
||||
|
||||
indent:
|
||||
function(state, textAfter) {
|
||||
// console.log(state.tokenStack);
|
||||
return myIndent(state,textAfter);
|
||||
}
|
||||
};
|
||||
});
|
||||
64
codemirror/mode/erlang/index.html
vendored
Normal file
64
codemirror/mode/erlang/index.html
vendored
Normal file
@@ -0,0 +1,64 @@
|
||||
<!doctype html>
|
||||
<html>
|
||||
<head>
|
||||
<meta charset="utf-8">
|
||||
<title>CodeMirror: Erlang mode</title>
|
||||
<link rel="stylesheet" href="../../lib/codemirror.css">
|
||||
<script src="../../lib/codemirror.js"></script>
|
||||
<script src="../../lib/util/matchbrackets.js"></script>
|
||||
<script src="erlang.js"></script>
|
||||
<link rel="stylesheet" href="../../theme/erlang-dark.css">
|
||||
<style type="text/css">.CodeMirror {border-top: 1px solid black; border-bottom: 1px solid black;}</style>
|
||||
<link rel="stylesheet" href="../../doc/docs.css">
|
||||
</head>
|
||||
<body>
|
||||
<h1>CodeMirror: Erlang mode</h1>
|
||||
|
||||
<form><textarea id="code" name="code">
|
||||
%% -*- mode: erlang; erlang-indent-level: 2 -*-
|
||||
%%% Created : 7 May 2012 by mats cronqvist <masse@klarna.com>
|
||||
|
||||
%% @doc
|
||||
%% Demonstrates how to print a record.
|
||||
%% @end
|
||||
|
||||
-module('ex').
|
||||
-author('mats cronqvist').
|
||||
-export([demo/0,
|
||||
rec_info/1]).
|
||||
|
||||
-record(demo,{a="One",b="Two",c="Three",d="Four"}).
|
||||
|
||||
rec_info(demo) -> record_info(fields,demo).
|
||||
|
||||
demo() -> expand_recs(?MODULE,#demo{a="A",b="BB"}).
|
||||
|
||||
expand_recs(M,List) when is_list(List) ->
|
||||
[expand_recs(M,L)||L<-List];
|
||||
expand_recs(M,Tup) when is_tuple(Tup) ->
|
||||
case tuple_size(Tup) of
|
||||
L when L < 1 -> Tup;
|
||||
L ->
|
||||
try Fields = M:rec_info(element(1,Tup)),
|
||||
L = length(Fields)+1,
|
||||
lists:zip(Fields,expand_recs(M,tl(tuple_to_list(Tup))))
|
||||
catch _:_ ->
|
||||
list_to_tuple(expand_recs(M,tuple_to_list(Tup)))
|
||||
end
|
||||
end;
|
||||
expand_recs(_,Term) ->
|
||||
Term.
|
||||
</textarea></form>
|
||||
|
||||
<script>
|
||||
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
|
||||
lineNumbers: true,
|
||||
matchBrackets: true,
|
||||
extraKeys: {"Tab": "indentAuto"},
|
||||
theme: "erlang-dark"
|
||||
});
|
||||
</script>
|
||||
|
||||
<p><strong>MIME types defined:</strong> <code>text/x-erlang</code>.</p>
|
||||
</body>
|
||||
</html>
|
||||
Reference in New Issue
Block a user