codemirror 5前端代码编辑器资料整理。

CodeMirror 是基于js的源代码编辑器组件,它支持javascript等多种高级语言,tampermonkey内置的代码编辑器就是基于它。它的按键组合方式兼容vim,emacs等,调用者还可自定义”自动完成“的列表窗口,自由度极高,相当成熟。

但是这个库也是问题多多,很需要深度配置、改良优化。

前排提示:由于信息闭塞,所以才研究了 codemirror 5。研究完才发现, codemirror 5官方基本处于弃坑状态,issue没人理。最新版本是 codemirror 6,仍在开发当中,但各方面效果已经,明显优于 codemirror 5。

0. 入门

入门很简单,可以F12观察官方demo是怎么实现的:

https://codemirror.net/ (codemirror 6

https://codemirror.net/examples/

https://codemirror.net/5/

关键代码:

<!-- Create a simple CodeMirror instance -->
<link rel="stylesheet" href="lib/codemirror.css">
<script src="lib/codemirror.js"></script>
<script>
  var editor = CodeMirror.fromTextArea(myTextarea, {
    lineNumbers: true
  });
  v
</script>

官方demo都是直接运行在文档页面上的。而且在编辑框内,显示的是demo自身的代码。有许多这样的小例子。

附本地运行方式:

  1. 从GitHub下载整个仓库,里面有些(比如核心库codemirror.js)是需要编译后才能运行,但有些就是普通的js代码(比如各种addon、mode),以及样式文件 codemirror.css。
  2. 从cdn或官方的demo示例中查找核心库地址:codemirror.js,这虽然是多模块编译出来的,但未经混淆,直接用vscode阅读修改并无不可,甚至效率更高。
  3. 编辑并预览 demo.html。

1. 需求:用任意键触发自动完成

https://stackoverflow.com/questions/13744176/codemirror-autocomplete-after-any-keyup

关键代码:

editor.on("keyup", function (cm, event) {
//  …… 判断条件,过滤一些按键,过滤某些上下文情况。
            CodeMirror.commands.autocomplete(cm, null, {completeSingle: false});
    });

如果只要ctrl+space触发自动完成的话,是这样配置的:

var editor=CodeMirror.fromTextArea(document.getElementById("code"),{
		……
		, extraKeys:{"Ctrl-Space":"autocomplete"}
		// 或者 :
		, extraKeys:{"Ctrl-Space":'Ctrl-Space': (cm) => 				 
			{
				cm.showHint()
			}}
		}	
	});

2. 让自动完成窗口同时 ① 包含 buffer 中提取的上下文关键词(anyword)②包含 javascript 语言的关键词。

https://discuss.codemirror.net/t/anyword-hinting-while-in-javascript-mode/1506/7

关键代码:

	function hintingFunction(cm) {
		const anyhint = CodeMirror.hint.anyword(cm, options)
		const jshint = CodeMirror.hint.javascript(cm, options)
		const words = new Set([...anyhint.list, ...jshint.list])
		if (words.size > 0) {
			return {
				list: Array.from(words), 
				from: jshint.from, 
				to: jshint.to
			}
		}
	}
	
	…… { completeSingle: false, hint: hintingFunction } …… 

如果只学习官方的短demo:

	CodeMirror.commands.autocomplete = function(cm) {
		CodeMirror.showHint(cm, CodeMirror.hint.anyword);
	}

,这样是不行的,因为只返回了anyword分析出的上下文关键词,导致javascript关键词被覆盖!

而 gpt 给的答案则更是搞笑:

	CodeMirror.commands.autocomplete = function(cm) {
		CodeMirror.showHint(cm, CodeMirror.hint.anyword);
		CodeMirror.showHint(cm, CodeMirror.hint.javascript);
	}

整天瞎编,机器人这会儿的鼻子已经到达银河系中心了吧!

注意需要正确引入 “codemirror5-master/mode/javascript/javascript.js”,不然没有 CodeMirror.hint.javascript(自动完成),还要引入 “codemirror5-master/addon/hint/javascript-hint.js”,不然也没有 mode:"application/javascript" (语法高亮)!

3. 换行回车时,保持原有缩进。

这是 tampermonkey 编辑器最让我不爽的一点:每次在方法之间换行,都把缩进吃了。后来干脆换用vscode + @require file:///本地文件.user.js 的方式,不再用它的内置编辑器。现在,我几乎站在了同一起点,自然要比前辈做得好!

查阅资料后发现,原来“把缩进吃了”是特性,而不是bug,关掉即可:

mode : ……
, smartIndent: false

真是杀马特啊。

可关掉之后,function() { 回车后,就不会自动添加应该有的缩进了,伤脑筋……

最后通过修改 codemirror.js,让杀马特indent只自动增、不自动减:

if (how == "smart") {
	……
	indentation = Math.max(state.indented, indentation);

4. 禁止自动滚动

给安卓用,需要禁止在点击屏幕、设置光标的时候,自动发生横向滚动,以便检阅代码。

复述需求:当移动设备用户检阅代码时,很有可能正在查看长代码行,到触碰到代码行底部时,可能意外导致横向滚动归零。

此功能看起来难于实现,实则很简单:新增options.scrollOnClick,未设置时,不触发 ensureCursorVisible 即可:

  function setSelectionNoUndo(doc, sel, options) {
  	……
	if(doc.cm && doc.cm.options.scrollOnClick) // 不自动触发横向滚动,除非打字。
    if (!(options && options.scroll === false) && doc.cm && doc.cm.getOption("readOnly") != "nocursor")
      { ensureCursorVisible(doc.cm); }
  }

5. 重排自动完成列表,使常用关键词前置化

比如 敲v,自动列表为:

请添加图片描述
不合理。

通过进一步修改上面的 hintingFunction 来完成:

 		if (words.size > 0) {
			var list = Array.from(words);
			console.log(list);
			if(list[0][0]=='v'){
				var varIdx = list.indexOf("var");
				if(varIdx>0) { // 如果关键词 var 排位大于零,则交换。
					var tmp = list[0];
					list[0] = "var";
					list[varIdx] = tmp;
				}
			}
			……

方法简单粗暴,但管用:
请添加图片描述

6. 多文档与编辑状态

像scintilla这种,是一个ui呈现,切换多份不同的文件buffer。

CodeMirror 有所不同,可以从多个 TextArea 创建多份 editor 实例,比较方便。

编辑状态:需要知晓当前文档是否可以:保存、撤销、重做等状态。

↳→→ 查看 文档是否被修改:editor.doc.isClean()

↳→→ 设置 文档已保存:editor.doc.markClean()

查看 当前修改堆栈:editor.doc.history

↳→→→ 分为 : editor.doc.history.done 撤销栈、 editor.doc.history.undone 重做栈,是否读取长度就可以了呢?不行,因为修改文本选择也会算入其中。因此需要筛选出其中的 changes:

请添加图片描述

不关键代码:

		function updatePreview() {
			console.log('编辑器状态 需要保存='+!editor.doc.isClean(), "可撤销="+backable(), "可重做="+forwardable())
		}

7. 实战之实现安卓端 用户脚本编辑器

webview-gm 可以让普通的安卓weview也支持运行用户脚本(如via浏览器)。webview-gm-demo 项目中自带的编辑器就是一个 editortext,可以用 codemirror 改进。

实测在安卓端发现一个比较严重的BUG,如果用 \t 缩进,则用输入法打字的时候,会自动删除 \t 后面打出来的字符,而且是持续化删除,不知如何解决,已在github上反馈问题,顺便帮via浏览器出出名,锦上添花。(android 10 chars got deleted automatically & continuously sometimes #7036

反馈问题后就睡觉了,第二天还是没消息。无奈只好自己下手。有人可能会问, – "为什么不用空格缩进呢? " – 空格缩进 太乱,有的用四个空格,有的用两个,甚至一个,不规范。而且用 tab 缩进还有一个空格缩进无法比拟的好处:如果需要将输入光标移动至行首,tab缩进的,只需点击第一个 tab 的前半部分即可;而多个空格缩进的,则需要精确点击第一个空格之前才行,实为反人类设计。

这种小问题无非是哪处开关问题。我用肉眼稍微扫描一遍代码,就已经定位问题所在:

option("inputStyle", mobile ? "contenteditable" : "textarea", function () {

通过在editor构造参数中传入 inputStyle : “textarea” 似乎即可解决问题。

然而…… 在 textarea 的编辑方式下,安卓上的文本选择没有了,无法选择代码段,这个bug一样严重。

所以……需要继续研究。需要开启打log模式,通过不断地运行、记录,来摸清代码运行路线,来定位问题症结所在。

最终,发现在 inputStyle : “contenteditable” 的编辑模式下,安卓输入法会将文本输入到 tab 元素之中,而tab元素有个 cm-text 属性,导致新输入的文本被忽略:

    function walk(node) {
      if (node.nodeType == 1) {
        var cmText = node.getAttribute("cm-text");
        if (cmText) {
          addText(cmText);
          return
        }
<span class="cm-tab" role="presentation" cm-text="	">    h</span>

可以看到,tab 元素里面其实还是四个空格,只不过用 cm-text=" " 标明了这其实是一个 \t 符号。可以修改相关代码,取消 cm-text 的遮蔽作用。

令人欣慰的是,codemirror 竟然会检测环境中的变量,比如在 via 浏览器中是这样的:
请添加图片描述
打出来后,似乎还能进一步读取对象的属性!

codemirror 6 更进一步,默认配置就是这样的,融合了anyword hints 与 javascript hints,var关键词也处于首位,而且还有方法模板、关键词双击高亮,更像ide了。

不过 codemirror 6 首页demo里的库是混淆过的,github 仓库里代码很少,不知是怎么一个结构。

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值