我有使用JavaScript的方案(称为LIPS),并且正在使用jQuery Terminal和新创建的multline命令示例编写多行解释器,该代码简单可防止Enter键的默认行为。
我的lisp的问题在于,如在GNU Emacs中那样,如果没有在输入时自动缩进的话,它看起来就不会很好。因此,我编写了简单的自动缩进,但是我不知道如何使其与GNU Emacs中的相同。我一直在寻找lisp-mode的源代码,但我不是emacs lisp专家,并且该代码不给我任何线索,知道缩进的正确逻辑是什么。
这是我的起始代码:
// lisp indentation function
function indent(term, level, offset) {
// offset is for prompt on first line
// level if for single indent of next line
// function return code before cursor
// to the beginning of the command
var code = term.before_cursor();
var lines = code.split('\n');
var prev_line = lines[lines.length - 1];
var parse = prev_line.match(/^(\s*)(.*)/);
var spaces = parse[1].length || offset;
var re_if = /(.*\(if\s+)\(/;
var m = prev_line.match(re_if);
if (m) {
spaces = m[1].length;
} else if (parse[2].match(/\(/)) {
spaces += level;
}
return spaces;
}
var term = $(selector).terminal(function(code, term) {
lips.exec(code, env).then(function(ret) {
ret.forEach(function(ret) {
if (ret !== undefined) {
env.get('print').call(env, ret);
}
});
}).catch(function(e) {
term.error(e.message);
});
}, {
name: 'lisp',
prompt: 'lips> ',
enabled: false,
greetings: false,
keymap: {
ENTER: function(e, original) {
if (lips.balanced_parenthesis(this.get_command())) {
original();
} else {
var i = indent(this, 3, this.get_prompt().length);
this.insert('\n' + (new Array(i + 1).join(' ')));
}
}
}
});
这是我的codepen demo,它具有您可以忽略的按键和按下键,重要的是keymap.ENTER
和indent
功能。
我的问题是我应该如何实施方案缩进?都有些什么样的规矩?我认为,只要知道算法,我就能使它起作用,但是可能存在很多极端情况,缩进应该如何起作用。
我的基本代码仅对每个换行符缩进2个空格,并与if
之后的第一个括号对齐,但仅对第一行对齐,因为它仅检查前一行。
可以使用的辅助函数是tokenize(code: string, extended: boolean)
,它返回带有{token, offset}
的字符串或对象数组(偏移量是字符串内令牌的索引)。
更新:
这是我更新的代码,唯一的特殊之处是if
,它现在可以与多行一起使用。
// return S-Expression that's at the end (the one you're in)
function sexp(tokens) {
var count = 1;
var i = tokens.length;
while (count > 0) {
token = tokens[--i];
if (!token) {
return;
}
if (token.token === '(') {
count--;
} else if (token.token == ')') {
count++;
}
}
return tokens.slice(i);
}
// basic indent
function indent(term, level, offset) {
var code = term.before_cursor();
var tokens = lips.tokenize(code, true);
var last_sexpr = sexp(tokens);
var lines = code.split('\n');
var prev_line = lines[lines.length - 1];
var parse = prev_line.match(/^(\s*)/);
var spaces = parse[1].length || offset;
if (last_sexpr) {
if (last_sexpr[0].line > 0) {
offset = 0;
}
if (['define', 'begin'].indexOf(last_sexpr[1].token) !== -1) {
return offset + last_sexpr[0].col + level;
} else {
// ignore first 2 tokens - (fn
var next_tokens = last_sexpr.slice(2);
for (var i in next_tokens) {
var token = next_tokens[i];
if (token.token.trim()) {
// indent of first non space after function
return token.col;
}
}
}
}
return spaces + level;
}
可以在此处测试代码:https://jcubic.github.io/lips/我是否错过了一些边缘情况,还是if
是唯一的特殊缩进情况?
答案 0 :(得分:0)
标准缩进随着换行符的放置而改变。缩进对齐意味着:
(one
two
three)
(one two
three)
您不这样做。不知何故,您正在使用两个空格缩进来表示隐式开始的特殊形式。
(define
two)
(define two
three)
现在适用的规则基本上是所有包含begin
,begin
,define
,lambda
和朋友之类的隐式let
的语法。我认为DrRacket会以“ def”,“ abd”和“ begin”开头的每个绑定执行此操作,这样您就可以制作def-system-call
,而实际上它会像define
那样缩进,而letx
则不会。
在附加语法的定义中可能有一些指示。例如。在Common Lisp中,您可以在宏中使用&body
而不是&rest
,它们代表其余元素,其区别在于&body
仅表示它应像特殊形式一样具有2个空格缩进像defun
。由于您使用的是自己的语言,因此可能会在您的语言中包含以下内容:)
答案 1 :(得分:0)
这是我基于@coredump链接的工作缩进:
function sexp(tokens) {
var count = 1;
var i = tokens.length;
while (count > 0) {
token = tokens[--i];
if (!token) {
return;
}
if (token.token === '(') {
count--;
} else if (token.token == ')') {
count++;
}
}
return tokens.slice(i);
}
function indent(term, level, offset) {
var code = term.before_cursor();
var tokens = lips.tokenize(code, true);
var last_sexpr = sexp(tokens);
var lines = code.split('\n');
var prev_line = lines[lines.length - 1];
var parse = prev_line.match(/^(\s*)/);
var spaces = parse[1].length || offset;
if (last_sexpr) {
if (last_sexpr[0].line > 0) {
offset = 0;
}
if (last_sexpr.length === 1) {
return offset + last_sexpr[0].col + 1;
} else if (['define', 'lambda', 'let'].indexOf(last_sexpr[1].token) !== -1) {
return offset + last_sexpr[0].col + level;
} else if (last_sexpr[0].line < last_sexpr[1].line) {
return offset + last_sexpr[0].col + 1;
} else if (last_sexpr.length > 3 && last_sexpr[1].line === last_sexpr[3].line) {
if (last_sexpr[1].token === '(') {
return offset + last_sexpr[1].col;
}
return offset + last_sexpr[3].col;
} else if (last_sexpr[0].line === last_sexpr[1].line) {
return offset + last_sexpr[1].col;
} else {
var next_tokens = last_sexpr.slice(2);
for (var i in next_tokens) {
var token = next_tokens[i];
if (token.token.trim()) {
return token.col;
}
}
}
}
return spaces + level;
}
可以在LIPS homepage上看到。