我是scheme-langserver作者,来贵宝地吸收一下想法

大家好,我是scheme-langserver作者。 想听听emacs社区有没有什么想法。 请循序渐进的批评,因为我知道还有好多bug没改……

1 个赞

虽然我不用scheme,不过在中文社区问,那建议多半就是能不能加强中文支持,有中文文档,中文菜单什么的。 现在代码里不怎么敢写中文了, 怕那天打开文件 里面中文就全变数字了

吸收想法, 这个词用的相当有包容力 :grinning:

草,要不要先简单介绍下 Scheme 和 LSP 实现了什么功能,虽然大部分人都应该知道 Scheme 是什么,不过也不太会专门去看完 README

试着编译了一下,看来不支持 macOS

一个是你 fork 的 chez-exe 大概原本对 macOS 适配就是有问题,macOS 上没 libtinfo 应当用 libncurse,我改了下参数编译出来,在编译 langserver 最后一步出现

clang: no input files

直接运行 run.wpo 看来明显还没支持 macOS

Exception occurred with non-condition value "init-thread-pool without pool-size only works for Linux null!"

懒得 debug compile-chez-program 的问题, 好像 readme 里提了这个问题。个人觉得编译成独立 executable 不是什么一定要有的功能,就是我用不了的话那我没法评价。

看了下问题来源是 ufo-threaded-function.chezscheme.sls:12

(define default-pool (init-thread-pool))

中文文档 我不清楚您的代码里面不敢写中文是什么意思,Chez对中文的支持还是相当好的,它的String模型是32-bit的,scheme-langserver也是支持中文的。在代码层面上,我甚至有一些注释是葡萄牙语的。

咳,讲个笑话:欧洲古代不识字,要记录一件事儿就路上随机抓个小男孩暴打一顿。20年后,记忆犹新嘿! 我来挨打的。

那我就在这里讲一下吧:

  1. scheme是lisp语言的一个dialect;
  2. LSP是帮助编辑器实现大量在IDE里面提供的功能,比如自动补全、跳转定义等等;

scheme-langserver是一个服务r6rs 标准scheme编程的LSP实现,它的实现纯用Chez Scheme的。它的特点是做了partial evaluation和abstract interpreter,这就区别于传统的REPL了。传统的REPL编程比如你在emacs里面用geiser,你要补全一个let语句里面的内容是不可能给你补全由let定义的identifier的,因为这个let还没写完不能执行嘛。实际上geiser-chezscheme的适配只能补全top-level的。 那在scheme-langserver下面,因为partial evaluation的一些方法(我不好意思谈是技术,因为实际上挺简单的),我就可以自动补全。具体的图呢,还是转移到README去看。 Scheme-langserver也装备了一个type inference,目的是更好、更有效的做自动补全。不过这个功能现在只是能用,还在完善。

2 个赞

你可以 bash .akku/env scheme --script run.ss 我没有macos那一套东西,所以很多东西没法测试。不过如果你愿意贡献一个方案的话我可以合并进去。

以及,从init-thread-pool来看的话我就知道了,因为那个库也是我写的。 默认要走linux的命令去获得核心数量。你可以关闭多线程功能。

我提过了,是你另外一个库 ufo-threaded-function 里调用了没参数的 (init-thread-pool) 才报错的,改 language server 参数没用。

改了 ufo-threaded-function 后,或是直接在 init-thread-pool 里硬编码 cpu 核心数,重新编译以后 bash .akku/env scheme --script run.ss 过一会就挂了。

所以我尽力了,我也没有功夫给你写的库做 code review,粗看下来理论上没有用什么不跨平台的特性,。

然后 Linux 的 prebuilt binary 也没法用,我用的公用服务器没 patchelf 也改不了

interpreter /nix/store/0wydilnf1c9vznywsvxqnaing4wraaxp-glibc-2.39-52/lib/ld-linux-x86-64.so.2

fu*k nix

看了下 init-server,所以目前连 command line arg parser 都没有,run.ss(apply init-server (command-line-arguments)) 就非常神必,默认 (command-line-arguments) 是空的那 enable-multi-thread? = #f 多线程就是关闭的,所以除了多线程之外一定还有 bug

sorry,那我也没有什么办法了。我刚刚还试了一下 scheme --script run.ss,这个命令在nixos下面是没有问题的。我也咩有苹果的机器。 sorry,没钱我很遗憾T^T

请学一下如何用免費 CI 还有 Makefile,直接拿编译自己出来的 binary 挂 release 连 interpreter 和 dynamic linked library 都不看是很不专业的

如果你愿意接受的话,我可以在 WSL 上试下改进编译流程,写个 arg parser,然后顺带看下 Chez 10.0.1 和 macOS 的问题,以及 Emacs eglot 支持的问题

可能你没怎么深入用 geiser,这个用 Emacs 实现其实不难的,由 geiser-syntax--scan-locals 在 emacs lisp 层面实现。实际上甚至只要能做到正确的 tokenizing (所以下面 define 语法故意写错了) 就能实现个七七八八,by which Emacs 本身的 font lock 已经实现了。

截圖 2024-10-18 上午1.05.19 截圖 2024-10-18 上午1.05.44

如上,easily proven to be false,当然 Common Lisp 的 sly 的确没做这功能。

是 partial parsing,partial evaluation 是另一个东西。

实际上我比较感兴趣的是 backward type inference 能做到多靠谱

1 个赞

这个实际上匹配的是所有的symbol吧?有去分析它们的有效范围么? scheme-langserver能做到的事情是:

(let ([ab...])
 ab)
(let ([abc ...])
 abc)

前面的ab不会补全成abc,后面的abc不会补全成ab。

截圖 2024-10-18 上午1.36.11

分析词法作用域非常简单,不需要实现完整 parser,预先定义 let 等关键字引入的局部变量在哪里找到就可以了

当然在 language server 里能 macro expand 的话,就能做到䃼全任意自定义 syntax 引入的局部变量,这个 geiser 就做不到了。不过,不完整的代码也没法 expand 吧?除非在 partial parsing 上加上 symbolic execution。

1 个赞

葡萄牙语一点不了解,我随机打几个字谷歌翻译成葡萄牙语看起来应该是和英语非常相近的语言。代码中有点不敢写中文,是因为写中文总会遇到一些莫名其妙的问题,我在二楼贴的是链接是最近遇到的一例,想必本坛很多人都为系统及各种软件中文化支持苦恼过,就不在举例了。

其实全用英文也不错,只是有时候某些函数命名要准确描述某个意思时写得有点太长了(英文太差原因),而中文我能立即想出四个字来描述这个意思。就一直没动力全换英文

NOTE:我刚刚注册这个论坛,昨天操作达到上限了,所以今天才回复。

您说的那个token的,能做不同library之间的导入导出么?以及请您给我指个路,我想看看是怎么实现的。我预估这它也是用了一些规则的东西来做,那对比问题就实际上看规则库完善程度以及parsing tolerant了。我后者做的不咋地,还在慢慢改,倒是主要想看看这个。

我刚刚实现step-by-step expander,自定义宏引入的局部变量应该是可以分析的,不过这个估计得年底了,现在还属于期货feature的阶段。关键的一个问题是展开过程是否可逆,因为我需要宏展开的结果和调用宏的代码对齐。这个东西预期只能处理syntax-rules,syntax-case我不知道怎么弄。

geiser-syntax--scan-locals 函数

显然不会跨 library,光是局部变量也不需要处理跨文件吧

ok,所以我们两个人都把问题放在一起说了。

  1. 我看到了这段:
(defsubst geiser-syntax--binding-form-p (bfs sbfs f)
  (and (symbolp f)
       (let ((f (symbol-name f)))
         (or (member f '("define" "define*" "define-syntax"
                         "syntax-rules" "lambda" "case-lambda"
                         "let" "let*" "let-values" "let*-values"
                         "letrec" "letrec*" "parameterize"))
             (member f bfs)
             (member f sbfs)))))

那个代码我挺理解为啥只处理这几种绑定的。

  1. 在局部变量补全方面,scheme-langserver绑定管理这些form,见这里,尽管这个文档有点过期了。

  2. 在文件间的library导入导出其实对你前文所说的自定义宏的问题是有帮助的,或者我认为是有帮助的,因为可以明确的隔离完成的和未完成的代码。这样,如果自定义宏的代码在另一个library中,它定义局部变量这个效果就可以做了。不过我前面也说了我这是“期货feature”。