为依赖Angular.js的上古项目给VSCode编写$scope定义跳转扩展插件

简介: 虽然Angular.js停止更新已经一年了,但依赖它的上古时代的项目并不少。由于都是使用js开发,很难为其提供很好的维护,所以直到今天开发维护也并不愉快。可以说没有开发插件的支持,再成熟老练的框架都发挥困难。

去年一月份,Angular.js就停止更新了,但由于年代久远,许多老项目还在使用。就像使用jQuery网站的数量到今天仍然比Vue+React+Angular的总和还多!有理由相信十余年的维护期,让使用Angular.js的网站数量不会逊于Angular,为什么Angular升级这么难呢?其实从Angular.js升级到Angular近似于让Angular.js换成Vue,变化实在太大,几乎没办法简单升级过去的。

但这么老的框架,开发生态是不是就很好呢?想多了!搜搜VSCode里关于Angular.js的扩展项吧:

image.png

几乎全都是一些代码片段、自动补全、模板这样的东西,连个变量跳转都没有啊。

那么接下来就讲讲如何为Angular.js做一个非常基本的关于$scope作用域变量的跳转。

初始化

VSCode插件开发最基本的是要装几个东西,Yeomangenerator-codevsce,都是npm里面的都需要全局安装,新建插件项目可以直接yo code,里面语言我这里就选择Typescript了。

然后就是具体实现了,做这种代码跳转插件呢主要是要实现vscode.DefinitionProvider接口,首先新建一个类文件,然后起一个类名叫AngularJsDefineProvider好了:

import * as vscode from "vscode"

class AngularJsDefineProvider implements vscode.DefinitionProvider {
   
   
    provideDefinition(document: vscode.TextDocument, position: vscode.Position): vscode.ProviderResult<vscode.Definition | vscode.DefinitionLink[]> {
   
   
        ...
    }
}

这个Provider其实也就一个provideDefinition()需要实现,但不是定义了VSCode就会加载这个Provider的,还需要去注册它,可以回到新建项目时它自带的main.ts,在里面activate()里面导入上面的AngularJsDefineProvider类,然后向vscode注册:

vscode.languages.registerDefinitionProvider({
   
   
    scheme: 'file', language: 'javascript'
}, new AngularJsDefineProvider())

这就算是完成初始化了,一般情况下注册东西都要去package.json里面再写点什么,但这里是语言定义区的注册,所以没有关系,下面看看具体如何实现:

参数解析

先说一说provideDefinition()这个方法本身,它有2个参数,还有个返回值:

  • document顾名思义,是文档对象,是触发跳转定义请求的文档,这个请求当然只可能由一篇文档发出,不可能好几篇文档里面都要求要跳往各自的地方,所以它不是数组。
  • position则是发出这个定义跳转的具体位置,这个位置他不会是一个点,比喻说你光标停在一个词中间,它会自动拿到整个词的position
  • 然后就是这个返回值vscode.ProviderResult<vscode.Definition | vscode.DefinitionLink[]>,这个ProviderResult就是个说明性的类型,其实内容就是它的泛型本身,也就是vscode.Definitionvscode.DefinitionLink[],而Definition就是Location | Location[],直接由new vscode.Location()实现的,为什么都有数组呢,这个很好理解,因为除了找定义之外,找引用也是由这个方法发起的,引用它就可能不止一个地方有了,所以要数组来包含多个位置。

然后还需要自己先在类里定义3个变量方便使用:

private readonly document: vscode.TextDocument
private readonly range: vscode.Range
private readonly search: string

为什么定义这些变量呢,首先这里不会只有一个方法,而只要处理这个定义跳转就免不了用document这个参数,所以要先在类里定义个document变量当做共享参数,以免以后这个对象传来传去的麻烦。

然后是范围和搜索文本,这个范围是其实就是选中的文本,当然也是很重要的,后面会用它扩大选取搜索更多的内容,而search就是具体的搜索内容,range是可以根据情况扩大的,而search就相当于是根据range选出来的文本。

下面就可以进行具体的操作了,不过还需要一些前置准备,例如验证条件和搜索方法的实现,来为最终的跳转查找铺平道路。

前置准备

写当然是从provideDefinition()写起,先验证一下到底有没有positionrange

if (position.character <= 0) {
   
   
    return
}
const range = document.getWordRangeAtPosition(position)
if (!range) {
   
   
    return
}

如果没有直接return即可。然后给前面定义的3个变量赋值:

this.document = document
this.search = document.getText(range)
this.range = range

然后需要看看,它是不是scope,那无论是定义还是引用前面必然都要有个$scope.`,至于其他地方变量也叫这个`$scope.xx不需要我们找,那也无所谓了毕竟概率很小。那么还有一个问题,怎么分辨选的那块代码是要找定义还是找引用,一般来说,这里是需要用抽象语法树(AST)来解析的,但我这个其实需求比较简单,直接根据range搜后面的文本就好了,定义后面肯定有个=嘛,至于重新赋值也不用太考虑,所以先要写个根据range搜上下文的方法:

/**
 * 在搜索字符串的前后剪裁字符串
 */
public strAt(index): string {
   
   
    if (this.range.start.character + index < 0) {
   
   
        return ''
    }
    if (index < 0) {
   
   
        const s = new vscode.Range(new vscode.Position(this.range.start.line, this.range.start.character + index),
            this.range.start)
        return this.document.getText(this.document.validateRange(s))
    }
    return this.document.getText(new vscode.Range(
        this.range.end,
        new vscode.Position(this.range.end.line, this.range.end.character + index)))
}

我简单设计了一个strAt(),根据document.getTextrange对象剪裁取值,比喻说aa.bb.c,光标在bbrange也就会在bb,只要调用这个strAt(2)就会获取向后获取到.c这段字符串,而只要strAt(-3)就能向前获取到aa.这段字符串。

那么下面就可以使用这个strAt()做验证、定义、引用的判断了:

if (this.strAt(-7) !== "&scope.") {
   
   
    return
}
if (this.strAt(1) === "=") {
   
   
    return this.findScopeReference()
}
return this.findScopeDefine()

可以看到有了这个方法真的超简单,那下面就可以专心实现findScopeReference()findScopeDefine()了:

定义与引用查找

到了这一步,其实都不需要用strAt()来进行上下文查找了,而是直接对整个document进行全文检索,正常来说其实要检索整个项目所有可查找的document,但是这里图方便,而且因为Angular.js模块化其实挺差,写多了都写到一个文件里去了,所以其实也没必要检索整个项目了,如果想检索一些特定文件可以用vscode.workspace.openTextDocument(uri)来打开具体的文档来进行检索,这里就不演示了,下面看看具体怎么做:

首先是查找定义:

public findScopeDefine(): vscode.Definition {
   
   
    const thisDocIndex = this.document.getText().indexOf(`scope.${
     
     this.search} = `)
    if (thisDocIndex === -1) {
   
   
        return
    }
    return new vscode.Location(vscode.Uri.file(this.document.fileName), new vscode.Range(
        this.document.positionAt(thisDocIndex + 6),
        this.document.positionAt(thisDocIndex + 6 + this.search.length)))
}

定义一般只有一个,直接indexOf需要找的东西就行了,然后用vscode.Location包一下就可以完成跳转了。

public findScopeReference(): vscode.Definition[] {
   
   
    const thisDocArr = [...this.document.getText().matchAll(new RegExp("scope." + this.search, "g"))]
    if (!thisDocArr.length) {
   
   
        return
    }
    return thisDocArr.map((item) => {
   
   
        return new vscode.Location(vscode.Uri.file(this.document.fileName), new vscode.Range(
            this.document.positionAt(item.index + 6),
            this.document.positionAt(item.index + 6 + this.search.length)))
    })
}

找引用就需用正则匹配多项了,找到后解构到数组里,再用map转成vscode.Location对象。

下面用GIF演示一下:

code-jump.gif

本文依照作者在2021年的一些开发经验,于2023年7月18日同时写作并发布在lyrieek的稀土掘金社区与阿里云开发者社区。

参考

目录
相关文章
|
3月前
|
Ubuntu 网络安全 PHP
如何使用vscode的Docker插件管理ubuntu 拉取服务器的镜像以及创建容器
本测试镜像旨在记录使用vscode的Docker插件拉取病创建Dockerfile,以及拉取镜像。
|
9月前
|
机器学习/深度学习 人工智能 自然语言处理
|
9月前
|
人工智能 JavaScript 关系型数据库
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
357 14
【02】Java+若依+vue.js技术栈实现钱包积分管理系统项目-商业级电玩城积分系统商业项目实战-ui设计图figmaUI设计准备-figma汉化插件-mysql数据库设计-优雅草卓伊凡商业项目实战
|
8月前
|
开发工具 git
vscode推送项目到github仓库故障解决1
本文介绍了如何优雅解决本地仓库与远程仓库历史记录不一致的问题,并提供避免未来问题的最佳实践。核心在于理解问题根源(如历史记录差异和常见原因),采用推荐的解决方案(先本地初始化再关联远程仓库),并遵循一致的工作流程、团队协作规范及熟悉 Git 命令。通过强制推送或合并无关历史记录等方式处理现有冲突,同时养成良好习惯以预防类似问题。
|
7月前
|
JavaScript 前端开发 程序员
甚至用不了五分钟就能学会vscode插件开发
本文介绍了VSCode插件的开发流程,从创建项目到最终发布。首先通过安装`yo`和`generator-code`脚手架工具初始化项目,选择JavaScript语言配置基础信息。接着,在`extension.js`中实现业务逻辑,例如将中文“变量”替换为“var”。通过F5进入调试模式验证功能。完成后使用`vsce`工具进行打包,解决可能遇到的版本不兼容或README文档问题。最后生成`.vsix`文件,可通过VSCode的“从VSIX安装”加载插件,实现开发闭环。进一步可将插件发布至官方市场供更多开发者使用。
|
9月前
|
人工智能 自然语言处理 前端开发
通义灵码插件在VSCode中的运用
借助通义灵码插件在VSCode构建新闻网,实现效率与智能的融合。VSCode作为前端开发的“瑞士军刀”,提供实时预览、智能辅助和高效管理功能;通义灵码插件通过自然语言交互生成代码,简化复杂任务并优化代码质量。两者结合形成从设计到部署的高效闭环,大幅减少基础编码时间,降低开发门槛,助力开发者聚焦业务逻辑与创新。
|
9月前
|
人工智能 小程序 程序员
【视频测评 DEMO 参考】VSCode 神级 AI 插件通义灵码:完全免费+实战教程+微信贪吃蛇小程序
VSCode 神级 AI 插件通义灵码:完全免费+实战教程+微信贪吃蛇小程序
705 8
|
8月前
|
JavaScript 前端开发 API
纯js轻量级页面顶部Loading进度条插件
纯js轻量级页面顶部Loading进度条插件
|
11月前
|
JavaScript 容器
带方向感知功能的js图片遮罩层插件
带方向感知功能的js图片遮罩层插件
|
11月前
|
JavaScript 前端开发
基于SVG的js圆形菜单插件
这是一款基于SVG的js圆形菜单插件。该js圆形菜单插件可以生成漂亮的圆形菜单效果,支持二级菜单,支持使用鼠标滚动切换菜单
213 16