首页
归档
友情链接
关于
Search
1
在wsl2中安装archlinux
116 阅读
2
nvim番外之将配置的插件管理器更新为lazy
86 阅读
3
2018总结与2019规划
65 阅读
4
PDF标准详解(五)——图形状态
43 阅读
5
为 MariaDB 配置远程访问权限
34 阅读
软件与环境配置
博客搭建
从0开始配置vim
Vim 从嫌弃到依赖
archlinux
Emacs
MySQL
Git与Github
AndroidStudio
cmake
读书笔记
编程
PDF 标准
从0自制解释器
qt
C/C++语言
Windows 编程
Python
Java
算法与数据结构
PE结构
Thinking
FIRE
菜谱
登录
Search
标签搜索
c++
c
学习笔记
windows
文本操作术
编辑器
NeoVim
Vim
win32
VimScript
emacs
elisp
linux
文本编辑器
Java
反汇编
读书笔记
OLEDB
数据库编程
数据结构
Masimaro
累计撰写
319
篇文章
累计收到
31
条评论
首页
栏目
软件与环境配置
博客搭建
从0开始配置vim
Vim 从嫌弃到依赖
archlinux
Emacs
MySQL
Git与Github
AndroidStudio
cmake
读书笔记
编程
PDF 标准
从0自制解释器
qt
C/C++语言
Windows 编程
Python
Java
算法与数据结构
PE结构
Thinking
FIRE
菜谱
页面
归档
友情链接
关于
搜索到
319
篇与
的结果
2023-03-04
从零开始配置vim(32)——最后再说两句
很抱歉我决定结束这个系列的内容了。原本我打算介绍markdown、orgmode相关的配置,甚至还打算介绍如何在vim 中使用 emacs 的 org-agenda 来进行日常的任务管理。但是出于一些原因我打算放弃了。首先如果将markdown 理解为另一种类似于HTML 的标记语言的话,我们在介绍LSP 的时候已经介绍过该如何新增新的编程语言的支持,再另外介绍Markdown 的配置就显得多余了。而且本系列也并不打算事无巨细的带领大家从零开始配置一套完整的配置,我仅仅希望通过这一系列的内容介绍一下vimscript 或者lua 接口以及vim 的一些特性,让大家看完之后又能力自行动手弄出一套属于自己的配置。至于orgmode 的内容,我发现目前还没有任何插件能完美的模拟emacs 的orgmode 功能。vim 上的插件也仅仅能做到渲染样式,语法高亮而已。也就没有必要单独介绍了。如果后续我能掌握 emacs 的话,再来介绍也不迟总之就是本系列到此结束了。一些建议不知道各位小伙伴在跟着我这一系列文章尝试自己配置vim 的时候有什么感觉?我当初在整理这些配置的时候发现它越来越像vs code ,甚至最近几年新推出的LSP以及 DAP 的一些插件几乎都是原生的用于 vscode 上的或者从它上面移植过来的。有些主题也是照搬 vscode 的。我们发现自己费劲心力终于将vim 变成的 vscode 。有没有觉得在做无用功?既然要将它变成 vscode 那为何不直接使用 vscode 呢?可能有人会说, vscode 对于vim的一些模式和 ex 命令的支持并不好。我想这就是我们使用 vim 的理由,也是vim 比其他编辑器强的地方。我们仅仅是在使用工具而已,哪个工具好用,哪个工具能帮助我们快速完成工作,那就用哪个。工具本身没有高低贵贱之分,只有合适与否的差异。作为程序员要拥抱新技术,千万不要抱着某个技术某个工具不放。也不要觉得用vim 的比用 vscode 或者其他编辑器的高级,就高人一等。vim自身也在吸收其他技术不断的成长,例如它从 vscode 那边学来了LSP 和 DAP 。这就有点像武侠小说中的吸功大法,集万物所长为我所用。另外一条建议就是千万不要拿我给出的配置直接来进行使用。这一套配置仅仅是为了教学使用,很多地方没有进行深度定制,并且基本采用白话的写法,完全不考虑封装性和程序设计,另外我也没有考虑通用性,很多小伙伴评论出现了各种各样的问题,最后就是它的效率也不算高。我也不希望自己的文章仅仅给各位小伙伴提供了一套配置。我更希望小伙伴们能通过这一系列文章学到一点东西,从这套配置中衍生出一套适合自己的内容。若干年以后,各位小伙伴在对vim有更深的理解回过头来看到这套配置时可能发出这样的声音:“这是什么破烂配置,连 xxx 的支持都没有;有些功能有时候会报错,我看看把它改好;启动时间咋这么慢,我能把它优化到xx毫秒;现在还在用xx技术早就落伍了,看我把它改成用xx技术”。(我自认为本系列最有价值的是开始配置之前,vim相关特性的介绍)最后的一条建议就是,如果各位小伙伴未来将长时间使用vim 进行代码的编写和日常的开发。那么我推荐使用一些社区比较活跃的第三方通用配置,例如我最近在使用的lunarVim。使用这类的配置有一些好处:不用费力折腾配置,节约时间学习高手的配置,提升自己对编辑器的审美。就像没学习vim之前我一直觉得使用编辑器用鼠标选中文本是天经地义的事,我习惯了它,甚至习惯了用鼠标翻页等操作,完全不知道这样有多么的浪费时间。通过高手配置可能能使你重新审视自己使用编辑器的习惯,从而找到一套真正适合自己的高效的文本操作术。社区活跃的话,除了问题不用自己死磕,可能有人能帮忙解决PS: 如果各位觉得我的教程不好或者有些内容没有提到,各位可以去看看lunarVim作者的另一个项目,Neovim-from-scratch 该项目也是从0开始配置vim,并且在油管上有对应的教学视频。后面的学习通过本系列的学习相信各位小伙伴已经有能力能看懂各种第三方配置的代码,能在此基础之上衍生出一套属于自己的配置。甚至能完全抛弃第三方配置独立弄出一套自己的配置。所以后面我推荐的学习路线就是:不断阅读vim官方手册熟练使用某一个第三方配置在熟练的基础之上根据自己的习惯来定制一些只属于自己的功能形成一套只属于自己的科学的、高效的文本操作习惯根据这套习惯尝试定制自己的配置在其他编辑器中通过一定的配置尝试复刻这一套科学而又高效的操作习惯目前我正在第三部分努力。希望本系列文章能带领大家真正入门vim ,不会再出现因为觉得难而中途放弃。最后祝愿各位小伙伴在vim的使用中能收获快乐,并坚持下去!
2023年03月04日
8 阅读
0 评论
1 点赞
2023-02-18
2023年阅读清单
2022 年总的来说读的书并不多,虽然上班通勤时间变长,按道理来说这段时间是读书的大好时光,但是我自己躲不过手机的诱惑,经常刷刷B站或者知乎,时间就过去了。想到这里我感觉有点后悔,大量的时间被浪费了。但是再怎么后悔2022已经过去,还是打起精神好好度过2023年才是真的。总的来说记录自己读书的清单并且写一些评语还是很有用的,有时候书读过后忘记了,为了写点评语我又回过头来看看书中的内容,并且为了这个目的有时候也会思考并且做些笔记,总之这个办法比起囫囵吞枣的堆读书数据来看收获更多,我还是坚持这个习惯《Unix传奇》本书的作者 Brain W.Kernighan 博士退休前在贝尔实验室计算科学研究中心工作。并且与肯.汤姆森和丹尼斯.李奇是很好的同事和朋友,作者亲眼见证了Unix的诞生以及发展。本书充满了有趣的回忆。书中从贝尔实验室的诞生开始介绍,并且介绍了实验室中一些科室中的有趣的人和事物,例如他们对必须挂工牌的反感以及对同事的恶作剧。实验室没有现在很多公司提倡的KPI、KOR等等考核指标,只有轻松的工作氛围,所有员工都在自己感兴趣的领域深入研究,谁也不知道能研究出什么样子。上层高管们也没有规定必须出现成果或者要产生商业价值,可以说贝尔实验室的工作氛围是所有打工人都梦寐以求的。只不过看着新进来的卡内基梅隆大学、斯坦福大学、伯克利大学加州分校的研究生,这是我等普通打工人无法进入的领域。贝尔实验室真是一个理想主义大放异彩的时代!先后诞生了Unix、C语言、管道、Grep、vi、yacc、lex、awk 、BSD Unix(虽然这个是因为版权问题由伯克利大学重新编写代码)、以及在此之上的BSD Socket、Linux。另外书中介绍的Unix从诞生之初就有许多先进的、划时代的设计理念。比如沿用至今的Unix哲学——“一个软件只用做好一件事情,不要试图在单个程序中完成多个任务”。还有类似于开创性的将文件内容和解析交给上层应用程序,在操作系统看来文件只是一堆二进制内容,操作并不关心。同时为了安全设计了沿用至今的文件权限的标识位。后续又发明了管道、grep、vi、yacc、lex、awk等等工具。作者按照时间发展的顺序清晰的讲解了unix的发展历史。虽然最后因为种种原因Unix不得不退出历史舞台,但是它的诞生种子已经在各行各业生根发芽并成长为参天大树。比如现在还存在的BSD Unix以及在其上发展的TCP/IP协议层的实现代码、以及后续诞生的Linux。为如今的互联网发展奠定了坚实的基础。书中也能学到一些设计理念,我总结的理念如下:清晰原则:代码尽量写的清晰易懂模块原则:每个程序只用做好一件事情,不要试图在单个程序中完成多个任务组合原则:不同程序之间通过接口相连,接口之间用文本进行通信。例如为了这个发明的管道。《穷查理宝典》本书是查理芒格本人的生平以及芒格发表的各路演讲、各色媒体对芒格本人评价的大杂烩。本书号称展示了芒格的处事智慧,谦逊的为人,令人敬佩的价值观等等,似乎看过之后对自己的人生有很大的帮助,但是在我看来这是言过其实,而且也对不起它在豆瓣的高分。全书读下来我认为有这么几个问题:本书是一个大杂烩,本来用很小的篇幅就能说明的问题硬是搞出500多页的内容,恨不得把关于查理芒格的所有东西都加进去。大量重复无用的篇幅读下来就是恶心、浪费时间。本书标榜的是介绍查理芒格的智慧以及处事哲学,但是里面充斥着社会上形形色色的人对芒格的评价。此举有点像为了凑字数骗稿费。本书最大的败笔是里面包含大量无用的插图,并且大段关于插图的解说,它们与正文杂糅在一起没有很好的区分,容易干扰思路。本书中芒格教导我们要谦逊、正直、好学这些老生常谈的就不说了。里面也有一些有价值的观点:哪年你没有破坏一个你最爱的观点,那么你这些年就白过了。(就像前几年网上一句话:所谓成长就是不断觉得过去的自己就是一个傻x)如果一件事是个坏主意,你不会做过头。但是如果一件事是一个好主意,蕴含着重要的正理,那么你就没办法忽略了。然后你就很容易做过头。所以呢,如果你把它们做过头了,那些好主意是让你遭受可怕后果的好方法(一个人太过于认为自己是正确的是十分危险的,为了达到这个正确的结果往往会不择手段。就像复仇者联盟里面的灭霸为了达到使宇宙可持续发展这种正义的目的不惜牺牲宇宙一半的生命。另外罗翔老师说过我们无法用一个非正义的手段来达到正义的结果,我想说的应该就是这种情况吧)你必须知道重要学科的重要理论并经常使用它——要全部用上,而不是只用几种。大多数人都只养成一个学科。在手里拿着铁锤的人看来,世界就像钉子。我这辈子遇到的聪明人,没有不每天阅读的,一个都没有。简化任务的最佳方法一般是解决那些答案显而易见的大问题(《矛盾论》中也提到矛盾分为主要矛盾和次要矛盾,应该首先解决主要矛盾)这本书虽然拖沓、啰嗦,但是仍然一些比较好的观点和见解,值得一读,但是话说回来我只推荐前几章,后面的演讲部分反复讲一些重复的观点,完全可以跳过。最终我自己给这本书6分或者6.5分。《魔鬼数学》本书是一本关于数学的科普书。就跟它的副标题——《大数据时代数学思维的力量》一样,主要侧重与数学思维在如今这个大数据时代日常生活的相关应用。本书没有很深奥的数学知识,你只要有中学数学程度就能看懂。书中通过对我们日常生活中常见的例子进行探讨分析最终引出相关数学概念并给出一些解法。通过例子你不一定能清楚的学到很深的数学概念,但是相信通过这本书你对数学会有一个重新的认识。至少会明白这么多年经历的数学教育并没有白费,可能想要更好的买菜真的要用到数学知识。学好数学真的会让我们在往后的日子中少踩一些坑。读完之后我感觉自己至少有下面的一些收获:对彩票和博彩不再感兴趣了,及时或者博彩的期望高出了投入,在没有足够多本钱进行大量随机试验的情况下仍然可能亏损。概率高并不一定会发生,只是重复的次数够多发生的次数比不发生的次数多双轨思维,白天证明晚上证伪。就像孔子所说的,吾日三省吾身。对线性预测的准确性不太确定了,买基金或者做其他投资的时候不会说前面几年的业绩一直在增长,后面一定会增长及时官方的统计数据是准确的,但是有时候也不能轻易下结论,因为需要考虑统计的样本是否具有普遍性。这就是所谓的数据不会骗人但是统计学会对平庸有更好的容忍度,因为回归理论最终都是要回归平庸的。刻苦即勇气,在长时间毫无进展的情况下仍然坚持也是一种优秀的品质。这种品质甚至比所谓的聪明更有用数学不是在象牙塔中的高深知识,而是一种人类常识的直观体现下面摘录一些我喜欢的句子从事数学研究的人经常会问“你的假设是什么?这些假设合理吗?” 这样的问题令人厌烦,但有时却富有成效数学就是一些常识只要你认为“某个东西有价值,因此多多益善”,就是一种线性推理用一个数除以另一个数只是单纯的计算。考虑清楚用什么除以什么才是真正的数学问题贝叶斯定理不仅可以被看作一个数学方程式,还是一种偏重于数值的规则,它告诉我们如何结合新的观察结果修正我们赋予事物的置信度我的座右铭是“如果你将不可能排出在外,那么剩下的,无论可能性多么的小,都必然是事实,除非它是你没考虑到的那种假设”如果在机会对你有利时投入足够多的资金,就能抵消任何可能出现的坏运气我们用数学方法证明了一个我们已知的法则:钱越多,你所能承受的风险也就越大。有钱人有足够的资金储备,可以承受偶尔的失利造成的损失,并且通过继续投资,最终变得更有钱优秀的特质不会持续存在,随着时间的推移,平庸这位不速之客会悄然登场实际上,生活中随着时间产生起伏变化的的任何东西,几乎都会受到回归效应的影响相关关系并不意味着因果关系期望值并不代表我们期望发生的结果,而是指在多次做出该决定后的平均结果尽管看不到明显进展,却仍然全神贯注、有条不紊地反复钻研某个问题,不放过所有可能取得突破的机会。当今的哲学家把这种品质称作勇气,它是数学研究的必备条件白天证明,晚上反证的做法不仅适用于数学,还可以对我们的社会、政治、科学与哲学理念施加压力。在白天时,尽可能相信自己的理念是正确的,但是到了晚上,则认真思考自己的理念是不是错误的。不要自欺欺人!《财富自由之路》之前网上流传着这么一句话“你永远挣不到认知以外的钱,凭运气挣的钱最后也会凭实力亏掉”。与之对应的,想要财富自由首先自己要有相应的认知。这本书就是在重塑自己的认知,以便自己能配的上未来的财富自由。这本书是李笑来在微信公众号上同名专栏的一系列文章的汇总。本书阅读起来比较流畅,并没有晦涩难懂的地方,读起来一气呵成。可能是最有效的人生道理往往是最朴素、流传最广的。书中并没有什么比较新颖的地方,都是一些经常在其他书中提到的一些概念。它就是一本人生哲理的汇编,作者对一些哲理结合现代社会和自己的生活经历给出了一些新的理解,并且给出了一些切实可行的实践方法。虽然读起来可能没有醍醐灌顶的感觉,但也是一本重塑认知的好书。本书中比较重要的观点主要有下面几点:注意力>时间>金钱。 在有限的时间内集中注意力干有利于自己成长的事情,或者说是专注自己的成长想要快速学会某个知识点,行动是最重要的,在行动中学习理论。我们只需要掌握某个学科的入门级知识,剩下的在实践中学习选择比努力重要,想要在未来选择正确,需要大量不同学科知识的积累财务自由只是人生的某个里程碑,与高考结束,大学毕业一样,是人生的一个过程并不是人生的终点投资的刚需是避险,永远不要压上全部身家自己要先成为贵人,然后才能源源不断的遇到贵人改变能改变的,接受不能改变的是幸福的前提在财务自由前,出卖自己的时间。需要考虑这个工作能否让自己获得成长《刻意练习》在我阅读 《财富自由之路》的时候里面提到一个观点,“学习学习再学习”。也就是我们先要学习如何学习,然后再来进行学习。这本《刻意练习》正是教我们如何正确的学习。就像它的副标题写的那样——如何从新手到大师。采用书中刻意练习的方式可以相对快速的学习新的技能。一般读这种讨论方法的书,我总会考虑这么几个问题。是什么?为什么?怎么做?什么是刻意练习呢?刻意练习与我们常规的练习方式有什么区别呢?刻意练习的反面是天真的练习。所谓“天真的练习”,基本上只是反复地做某件事情,并指望只靠那种反复,就能提高表现和水平。与之相对的是有目的的练习,有目的的练习具有以下特点:有目的的练习具有明确的目标有目的的练习是专注的有目的的练习包含反馈有目的的练习需要走出舒适区刻意练习与有目的的练习又有区别。1。 首先刻意练习需要一个已经得到合理发展的行业或领域。也就是说,在那一行业或领域之中,最杰出的的从业者已经达到一定程度的表现水平,使他们与其他刚刚进入该行业或领域的人们明显的区分开来,例如书中经常举例的体育界、音乐界其次刻意练习需要一位能够布置练习作业的导师。这个导师必须已经达到一定的水平,并且有一些可以传授给别人的有益的练习方法。刻意练习有下面几个特点:刻意练习发展的技能,是其他人已经想出怎样提高的技能,也是已经拥有一套行之有效的训练方法的技能。刻意练习发生在人们的舒适区之外,而且要求学生持续不断地尝试那些刚好超出他当前能力范围的事物。刻意练习包含得到良好定义的特定目标,通常还包括目标表现的某种方便;它并非只想某些模糊的总体改进。刻意练习是有意而为的,也就是说,它需要人们完全的关注和有意识的行动。刻意练习包含反馈,以及为应对那些反馈而进行的调整的努力。刻意练习既产生有效的心理表征,又依靠有效的心理表征。刻意练习通过着重关注过去获得的技能的某些特定方面,致力于有针对性的提高那些方面,并且几乎总是包括构建或修改那些过去已经获取的技能。那么为什么要用刻意练习的方法呢?自然是为了有效的提升自己的某项技能。最后一个问题,该如何在生活中使用刻意练习的法则呢?看到之前关于刻意练习的描述,似乎我们生活中很多场景用不了刻意练习。比如我想学习英语,能脱离字幕看懂英文电影。或者能直接阅读英文的咨询或者英文书籍。又或者我们想学编程来提高自己的工作效率。这两个领域似乎都不算合理发展的行业,无法界定谁是最出色的,而且也没有所谓的导师来引领。如果本书仅仅到这里我想它是一本浪费时间的垃圾书。但是书中也给了我们一些建议,针对一些无法使用刻意练习的场景的一些建议。首先需要目标,也就是我们希望通过练习达到哪种水平然后尝试寻找导师。找到当前领域一些相对杰出的人,学习他们是如何掌握这项技能的。有的地方可以依葫芦画瓢。如果没有导师,需要牢牢记住3F原则,即 focus、feedback、fix。专注、反馈和修正总体来说,这本书干货确实不多。花了大量的篇幅在阐述天才是天生的还是通过后天的努力练习来获得天赋的,以此来给刻意练习背书。对我来说这些并不是我关心的。我只关心我应该如何做才能快速提升自己的能力。这方面书中并没有过多的阐述。如果想通过这本书获得通用的学习方法那你可能要失望了,你只能根据这些原则来自己制定自己的学习计划并努力付诸实施。然后通过大量的练习来达到目的。中间可能仍然会走弯路,甚至没有反馈而放弃。最后我给这本书的定位是可以读也可以不读。读完有那么一点收获但是不多。
2023年02月18日
4 阅读
0 评论
0 点赞
2023-02-01
从零开始配置vim(31)——git 配置
很抱歉又拖更了这么久了,在这个新公司我想快速度过试用期,所以大部分的精力主要花在日常工作上面。但是这个系列还是得更新下去,平时只能抽有限的业余时间来准备。这就导致我写这些文章就慢了一些。废话不多说,咱们正式开始有关git相关的配置。这些配置都是根据我自身使用习惯来定义的,不一定符合各位的习惯,各位可以根据自身的习惯来调整gitsigns第一个要推荐的插件是 gitsigns。就像它的名字一样,该插件可以将最近的更改以标签的形式展现出来方便我们查看。我们可以使用这样的代码进行安装 use {'lewis6991/gitsigns.nvim' }。需要注意最新的版本需要 neovim 的版本在0.7 以上。安装完成之后我们通过配置 require('gitsigns').setup()来使用它。这样我们就可以通过 Gitsigns toggle_signs来打开或者关闭符号显示了。除了采用最基本的符号显示以外,它还可以对改变位置的行号进行标记以及高亮显示变更的行。这两个功能可以通过 Gitsigns toggle_numl和 Gitsigns toggle_linel来打开,打开之后显示如下:从图中可以看到,更改行行号被用绿色显示了出来并且更改行进行了高亮显示另外它还有其他的显示效果:它主要的一些显示功能主要有下面几个:toggle_signs: 显示变更记录toggle_numl: 显示变更行号toggle_linel:高亮变更的行toggle_delete: 显示被删除的行,以红色背景高亮显示toggle_word_diff: 在两行分别显示修改前和修改后的内容toggle_current_line_blame: 在对应行后面显示提交记录我们将所有的这些功能都打开将得到这么一个效果是不是看着有点乱?这是我修改了一处的,一旦修改多了看着会更混乱。所以我自己的经验告诉我在这个buffer里面最好是只打开 signs、numl、linel、current_line_blame的功能,其他的都关掉。也就是在当前buffer中只显示现在的代码,然后辅助以简单的符号来显示哪行是新加的,哪行被删除了,哪行被修改了,至于修改前是什么样子的,我可以通过其他方式来查阅。所有内容都在一个buffer 中显示会比较乱,不利于阅读代码。我们可以通过配置将我们要显示的内容进行定义,也可以定义使用何种图标来表示修改记录。我们采用如下的配置gitsigns.setup({ signs = { add = {hl = 'GitSignsAdd' , text = '+', numhl='GitSignsAddNr' , linehl='GitSignsAddLn'}, change = {hl = 'GitSignsChange', text = '│', numhl='GitSignsChangeNr', linehl='GitSignsChangeLn'}, delete = {hl = 'GitSignsDelete', text = '-', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLn'}, topdelete = {hl = 'GitSignsDelete', text = '-', numhl='GitSignsDeleteNr', linehl='GitSignsDeleteLn'}, untracked = {hl = 'GitSignsAdd', text = '+', numhl='GitSignsAddNr' , linehl='GitSignsAddLn'}, changedelete = {hl = 'GitSignsChange', text = '~', numhl='GitSignsChangeNr', linehl='GitSignsChangeLn'}, }, signcolumn = true, -- Toggle with `:Gitsigns toggle_signs` numhl = true, -- Toggle with `:Gitsigns toggle_numhl` linehl = true, -- Toggle with `:Gitsigns toggle_linehl` word_diff = false, -- Toggle with `:Gitsigns toggle_word_diff` watch_gitdir = { interval = 1000, follow_files = true }, attach_to_untracked = true, current_line_blame = false, -- Toggle with `:Gitsigns toggle_current_line_blame` current_line_blame_opts = { virt_text = true, virt_text_pos = 'eol', -- 'eol' | 'overlay' | 'right_align' delay = 1000, ignore_whitespace = false, }, current_line_blame_formatter = '<author>, <author_time:%Y-%m-%d> - <summary>', sign_priority = 6, update_debounce = 100, status_formatter = nil, -- Use default max_file_length = 40000, -- Disable if file is longer than this (in lines) preview_config = { -- Options passed to nvim_open_win border = 'single', style = 'minimal', relative = 'cursor', row = 0, col = 1 }, yadm = { enable = false }, })其中大部分都是官方给出的默认配置,我只是在此基础之上改了 signs在各种状态下显示的图标,以及显示哪些内容。主要是使用 + 来表示新增、| 来表示修改、- 表示删除。除了显示以外它有一个重要的功能就是在各种修改状态之间跳转,例如调用 next_hunk来跳转到下一个更改位置。并且它也集成了一些git的操作。我们对常用的操作定义一些快捷键on_attach = function() vim.api.nvim_set_keymap("n", "<leader>gj", "<cmd>Gitsigns next_hunk<CR>", {silent = true, noremap = true}) vim.api.nvim_set_keymap("n", "<leader>gk", "<Cmd>Gitsigns prev_hhunk<CR>", {silent = true, noremap = true}) vim.api.nvim_set_keymap('n', '<leader>hs', ':Gitsigns stage_hunk<CR>', {silent = true, noremap = true}) vim.api.nvim_set_keymap('v', '<leader>hs', ':Gitsigns stage_hunk<CR>', {silent = true, noremap = true}) vim.api.nvim_set_keymap('n', '<leader>hr', ':Gitsigns reset_hunk<CR>', {silent = true, noremap = true}) vim.api.nvim_set_keymap('v', '<leader>hr', ':Gitsigns reset_hunk<CR>', {silent = true, noremap = true}) vim.api.nvim_set_keymap('n', '<leader>hS', '<cmd>Gitsigns stage_buffer<CR>', {silent = true, noremap = true}) vim.api.nvim_set_keymap('n', '<leader>hu', '<cmd>Gitsigns undo_stage_hunk<CR>', {silent = true, noremap = true}) vim.api.nvim_set_keymap('n', '<leader>hR', '<cmd>Gitsigns reset_buffer<CR>', {silent = true, noremap = true}) vim.api.nvim_set_keymap('n', '<leader>hp', '<cmd>Gitsigns preview_hunk<CR>', {silent = true, noremap = true}) vim.api.nvim_set_keymap('n', '<leader>hb', '<cmd>lua require"gitsigns".blame_line{full=true}<CR>', {silent = true, noremap = true}) vim.api.nvim_set_keymap('n', '<leader>tb', '<cmd>Gitsigns toggle_current_line_blame<CR>', {silent = true, noremap = true}) vim.api.nvim_set_keymap('n', '<leader>hd', '<cmd>Gitsigns diffthis<CR>', {silent = true, noremap = true}) vim.api.nvim_set_keymap('n', '<leader>hD', '<cmd>lua require"gitsigns".diffthis("~")<CR>', {silent = true, noremap = true}) vim.api.nvim_set_keymap('n', '<leader>td', '<cmd>Gitsigns toggle_deleted<CR>', {silent = true, noremap = true}) vim.api.nvim_set_keymap('o', 'ih', ':<C-U>Gitsigns select_hunk<CR>', {silent = true, noremap = true}) vim.api.nvim_set_keymap('x', 'ih', ':<C-U>Gitsigns select_hunk<CR>', {silent = true, noremap = true}) end这里是我将官方给出的配置直接粘贴了过来,这堆快捷键定义的操作中我最常用的就是 next_hunk和 prev_hunk来在各种修改之间进行跳转。虽然偶尔也用用 diffthis来显示差异,但这部分我更喜欢使用我接下来介绍的插件diffview这个插件从名字上看就知道是专门用来查看版本差异的插件。与前面介绍的 gitsigns插件相比它有下面几个优点:它是专门用来显示差异的,与gitsigns相比,显示的更加明显它可以在文件树中显示有变更的文件它可以做到任意版本之间的差异对比它可以显示单个文件的版本提交记录它还有另外的功能,可以由各位小伙伴根据官方文档自行了解。它的使用方式如下::DiffviewOpen显示当前与上一个版本之间的差异:DiffviewOpen + 版本号 可以显示当期与某一个特定版本的差异,例如 :DiffviewOpen HEAD~2或者:DiffviewOpen 906ddac317来查看版本差异● :DiffviewFileHistory + 文件名 来查看某个文件的版本差异因为它比较简单,具体的用法就不在这里演示了。关于它的配置,我自己认为平时很少使用,而且默认的配置已经够用了,也不太需要花精力为它优化快捷键了。lazygitlazygit是一个非常好用的git客户端,可以方便的进行提交、回滚、查看变更等git操作。这里我不推荐什么插件,因为它本身已经很强大了,而且脱离vim它也可以很好的工作。想来想去只有配置一个快捷键来快速打开 lazygit 的终端不知道各位小伙伴是否还记得之间介绍的 toggleterm 插件,我们将要依赖它来快速启动local lazygitterm = Terminal:new({ cmd = 'lazygit', direction = 'float' }) function lazygit_toggle() lazygitterm:toggle() end vim.api.nvim_set_keymap("n", "<leader>lg", "<Cmd>lua lazygit_toggle()<CR>", {noremap = true, silent = true})通过这样简单的配置我们已经可以使用 <leader>lg来快速启动 lazygit的客户端了。至此我们关于git的配置就完成了。一般我的使用习惯是使用 gitsigns来在更改中进行跳转,用于提交前或者合并分支前的代码审查,做到提交和合并都心中有数。在发生bug要回溯代码并且查看当前与没有问题的版本之间的差异会用到 diffview插件。在进行提交、合并、回溯等git相关操作时会使用到 lazygit。各位小伙伴也可以根据自己的使用习惯来定制这一部分的配置。
2023年02月01日
7 阅读
0 评论
0 点赞
2022-12-30
从零开始配置vim(30)——DAP的其他配置
很抱歉这么久才来更新这一系列,主要是来新公司还在试用期,我希望在试用期干出点事来,所以摸鱼的时间就少了。加上前面自己阳了休息了一段时间。在想起来更新就过去一个多月了。废话不多说了,让我们开始进入正题。在前一章,我们谈论了如何在 neovim 中使用cpptools 这个DAP 的适配器对代码进行调试,目前针对编译型和解释型语言来说我们都有了对应的方法来配置调试器对其进行调试。本节将要介绍关于dap的其他一些功能,主要包括 repl窗口和 gdb的集成repl 窗口什么是 repl 呢?它的全称是 Read Eval Print Loop 中文一般翻译为交互式解析器,可能看到这你还是一脸懵逼,你可以想想 python 或者nodejs,在控制台输入python 就可以进入到它的交互式解析器中,随着我们输入python 的语句,它会实时的给出运行的结果。交互式解析器就是这么一个东西,输入命令,它给你一个实时的结果。在调试中使用交互式解析器还是很有用的,比如我想显示当前某个变量的值,当前执行到哪个语句了等等。nvim-dap已经提供了一个内置的 repl 窗口,我们每次启动调试的时候都会看到它每次都会创建一个新的名为 dap-repl的buffer。之前我们没怎么关注它主要还是因为那个时候的重点在于如何进行调试,现在我们将要来优化这部分的显示和功能。首先我们发现每次调试结束的时候这个buffer 都会被遗留,需要我们手动的进行关闭,除了针对buffer通用的 :q 或者 :bd命令进行关闭,还可以使用 :lua require('dap').repl.close()。当然也可以配套使用 :lua require('dap').repl.open()来打开一个 repl的窗口,既然每次都会自动新建,那么这里我们就不需要进行新建,主要用于想办法关闭就可以了。还记得之前介绍 nvim-dapui 插件的时候介绍的那两个监听函数吗,同样的我们要在监听调试结束的函数中添加代码来关闭repl 窗口,函数的整个代码如下dap.listeners.before.event_terminated["dapui_config"] = function() dapui.close({}) dap.repl.close() end dap.listeners.before.event_exited["dapui_config"] = function() dapui.close({}) dap.repl.close() end再启动调试的时候发现它已经可以在调试结束的时候自动关闭了。还有另外一个问题就是我不太喜欢现在这样在最下角显示 repl。我希望它能够在最下方以整行的形式显示。或者可以方便的不显示,只有在需要的时候显示。要达成这个目的我们需要修改 dapui 的配置。dapui 的配置主要以 element为基础,每个 element 代表一个提供对应功能的窗口。我们先来根据它默认的配置来讲解每部分的含义require("dapui").setup({ icons = { expanded = "", collapsed = "", current_frame = "" }, mappings = { -- Use a table to apply multiple mappings expand = { "<CR>", "<2-LeftMouse>" }, open = "o", remove = "d", edit = "e", repl = "r", toggle = "t", }, -- Use this to override mappings for specific elements element_mappings = { -- Example: -- stacks = { -- open = "<CR>", -- expand = "o", -- } }, -- Expand lines larger than the window -- Requires >= 0.7 expand_lines = vim.fn.has("nvim-0.7") == 1, -- Layouts define sections of the screen to place windows. -- The position can be "left", "right", "top" or "bottom". -- The size specifies the height/width depending on position. It can be an Int -- or a Float. Integer specifies height/width directly (i.e. 20 lines/columns) while -- Float value specifies percentage (i.e. 0.3 - 30% of available lines/columns) -- Elements are the elements shown in the layout (in order). -- Layouts are opened in order so that earlier layouts take priority in window sizing. layouts = { { elements = { -- Elements can be strings or table with id and size keys. { id = "scopes", size = 0.25 }, "breakpoints", "stacks", "watches", }, size = 40, -- 40 columns position = "left", }, { elements = { "repl", "console", }, size = 0.25, -- 25% of total lines position = "bottom", }, }, controls = { -- Requires Neovim nightly (or 0.8 when released) enabled = true, -- Display controls in this element element = "repl", icons = { pause = "", play = "", step_into = "", step_over = "", step_out = "", step_back = "", run_last = "", terminate = "", }, }, floating = { max_height = nil, -- These can be integers or a float between 0 and 1. max_width = nil, -- Floats will be treated as percentage of your screen. border = "single", -- Border style. Can be "single", "double" or "rounded" mappings = { close = { "q", "<Esc>" }, }, }, windows = { indent = 1 }, render = { max_type_length = nil, -- Can be integer or nil. max_value_lines = 100, -- Can be integer or nil. } })首先最上面的 icons 表示,各个部分显示的图标,这里分别定义了展开的,合并的以及当前位置的图标信息,我们可以观察一下变量栏或者调用栈显示信息的左侧就可以看到这里定义的图标。mappings 代表的是部分窗口动作定义的快捷键。例如上面定义的 expand = { "\<CR>", "" }表示可以在待展开项上按下回车或者鼠标左键双击来展开。element_mappings表示的是我们为某些窗口特意定制的一些快捷键。例如上面注释的是针对调用栈定义的快捷键。layouts代表布局,每个布局都有一个子table,而每个子table主要由 elements、size、position组成,它们分别代表采取该种布局的元素(也可以说是窗口),窗口大小以及窗口的位置。窗口一般通过id来描述,每种窗口都有固定的ID,根据官方文档的描述,它支持这么几种窗口:scopes显示全局或者当前局部变量,它支持的操作主要是 edit编辑变量的值、expand展开结构化的变量、repl将变量拷贝到repl窗口stacks显示当前正在运行的线程以及它们对应的调用栈,它主要支持的操作是 open :运行代码到当前被选中的位置, toggle:打开或者关闭该窗口watches显示我们需要追踪的变量,它支持的主要操作是 edit: 输入想要追踪的变量或者给对应的变量赋值。expand: 展开结构化的变量,remove:删除当前监视的变量,repl:将变量拷贝到repl窗口breakpoints显示当前激活的断点。它支持的主要操作有 open:执行代码到当前选中的断点处, toggle :激活或者使当前断点无效repl显示repl窗口console显示控制台窗口这些窗口的这些操作的快捷键我们已经通过上方的 mappings做了定义了,只要保持光标在对应窗口然后按下快捷键就可以执行对应的窗口命令了。如果想要单独对窗口进行快捷键定义可以在element_mappings 中被注释的代码controls 部分配置的是在repl窗口上方显示的那一堆调试按钮。由于我们定义了一些快捷键,这些按钮没太大的作用。这里我不需要它显示调试用的按键,所以我就在 controls 项中设置 enabled = false 禁用它。floating、window、render则定义的是悬浮窗口样式和普通窗口的一些样式。这里就不深究了。下面是改造之后的配置dapui.setup({ icons = { expanded = "", collapsed = "", current_frame = "" }, mappings = { -- Use a table to apply multiple mappings expand = { "<CR>", "<2-LeftMouse>" }, open = "o", remove = "d", edit = "e", repl = "r", toggle = "t", }, layouts = { { elements = { { id = 'scopes', size = 0.35 }, {id = "stacks", size = 0.35}, {id = "watches", size = 0.15}, {id = "breakpoints", size = 0.15}, }, size = 40, position = "left", }, { elements = { "repl" }, size = 5, position = "bottom", } }, controls = {enabled = false}, floating = { max_height = nil, -- These can be integers or a float between 0 and 1. max_width = nil, -- Floats will be treated as percentage of your screen. border = "single", -- Border style. Can be "single", "double" or "rounded" mappings = { close = { "q", "<Esc>" }, }, }, windows = { indent = 1 }, })repl 窗口的主要命令如下:.exit: 退出/关闭一个 repl 窗口.c/.continue: 继续执行代码.n/.next: 执行下一行代码.into: 跳转到函数中继续执行.out: 跳出函数.scopes: 打印当前栈的一些变量信息.threads: 打印线程信息.frames: 打印当前线程的调用栈.capabilities: 打印当前适配器实现的一些功能.p 暂停当前运行的程序更多的命令可以通过在 repl窗口中输入 .help查看看了这么多无聊的文字描述,不知道小伙伴们有没有觉得头晕眼花呢?我们来通过实际的例子来看看如何应用这些内容来进行调试。示例1:调试单线程死循环假设有一段程序在不知不觉中被写成死循环了,程序无法正常执行下面的操作,我们以下面的程序为例#include <stdio.h> #include <unistd.h> void loop_forever(){ for(unsigned int i = 10; i >= 0; --i){ // do something sleep(0.1); } } int main (int argc, char *argv[]) { // do something loop_forever(); printf("do other things\n"); return 0; }本来我们想的是它执行完函数 loop_forever之后会执行接下来的操作,但是我们死活看不到它执行后面的操作,这个时候我们意识到它可能在某个地方陷入死循环,无法出来了,假设前后都有大断的代码,无法快速定位到死循环的位置,该如何处理这种情况呢?我们先通过<F5>来执行操作,然后在 repl 中输入 i 进入插入模式,然后执行.p 中断当前程序执行。此时程序已经断了下来,接着我们输入.frames 查看当前调用栈信息。我们发现此时程序停留在loop_forever 函数的 sleep 中,我们在栈中找到 sleep 的位置并按下回车,这个时候我们发现程序执行到了 sleep() 函数处了。这个时候我们在这里按下<F9>下一个断点,接着使用 <F5> 继续运行到断点位置停止,这个时候我们通过实时显示的 i值已经发现问题所在了。原来i 递减到0之后,继续递减,因为它是无符号数所以永远无法达成小于0 的条件。示例2:调试多线程死锁我们以下面一个多线程程序为例#include <cstdio> #include <thread> #include <vector> using namespace std; void func(int thread_id){ int var = thread_id; while(true); } int main (int argc, char *argv[]) { vector<thread> vec; for(int i = 0; i < 5; i++){ vec.push_back(thread(func, i)); } // join for(int i = 0; i < 5; i++){ vec[i].join(); } return 0; }因为使用了c++ 相关的内容和 thread 库,因此编译的时候需要使用 g++ 并且指定链接 pthread 库,我们采用 g++ main.cpp -g -o main -lpthread 来编译它我们还是按照之前的步骤,先按下 .p来暂停程序,这个时候我们发现它会提示我们需要暂停哪个线程,遗憾的是根据线程的id还没法判断具体哪个是子线程哪个是主线程。这里我们随便选一个暂停。然后执行 .threads查看当前线程信息,在某个线程下使用回车键可以看到调用的函数栈。我们发现子线程卡在while 这句话,我们还是一样在卡主的位置按下回车跳转到对应代码位置,在此处下一个断点。然后我们在对应线程位置按下 o 命令来继续执行之前暂停的线程。这样就完美的找到了线程卡死的位置了。后面可以使用 .c 来继续执行所有被中断的线程nvim-gdb 插件该插件提供了一种方式,可以直接在neovim中进入gdb的session。例如我们可以通过命令 :GdbStart gdb -q a.out来启动一个gdb会话,并且关联了一个 a.out 的程序。后续可以直接使用gdb相关的命令来启动调试这个程序。我们可以使用如下的代码来安装它use {"sakhnik/nvim-gdb", run="./install.sh"}我们先来试试效果,直接使用快捷键 <leader>dd 来加载一个程序进行调试。进入到gdb会话之后可以使用gdb 的命令。例如我们使用 b main来在 main函数的位置打一个断点,然后通过r来启动程序运行到断点处。接着可以使用 n来执行下一步或者使用 c来直接运行到下一个断点。最后可以使用 q退出基础配置我们发现使用 nvim-gdb 插件的时候会在对应代码位置显示断点或者当前执行行。这里我们对它做一些配置,先统一使用nvim-gdb和 nvim-dap这两种情况下的显示信息。先创建一个新的配置文件为 nvimgdb.lua作为它的配置文件。因为它暂时还不支持lua的配置所以这里我们使用vim原生的写法。vim.cmd([[ let g:nvimgdb_config_override = { \ 'sign_breakpoint': [''], \ 'sign_current_line': '', \ } ]])在这里定义了断点和执行到当前行的配置,这里只能统一图标,还无法做到完全像使用 dap那样显示。另外我们可以在 nvimgdb_config_override 这个变量中定义如下快捷键以保证在调试时拥有同样的体验 \ 'key_next': '<F10>', \ 'key_step': '<F11>', \ 'key_continue': '<F5>',最后我们发现当我们输入文件名进行调试的时候,它会一直闪屏。这是因为它会不断根据我们输入的内容在文件系统中匹配合适的可执行文件,为了解决闪屏问题我们屏蔽它的这个特性,我们可以使用如下的配置来解决这个问题 let g:nvimgdb_use_find_executables = 0 let g:nvimgdb_use_cmake_to_find_executables = 0显示窗口的配置定义了显示形式,我们来定义显示的窗口,这里我让它显示常用的像调用栈,变量,以及watch窗口。我们先使用如下代码来将窗口分割为左右两个部分 let w:nvimgdb_termwin_command = "rightbelow vnew" let w:nvimgdb_codewin_command = "vnew"根据官方文档的描述,nvimgdb_termwin_command 是终端窗口,用来显示repl 相关信息并且与用户交互,后面我们可以对它进行切割用于显示其他信息。nvimgdb_codewin_command 是源代码窗口。这两句代码可以形成一个左右分屏的界面,左侧显示代码,右侧显示repl窗口。在gdb成功加载之后,我们可以使用命令 :GdbCreateWatch info locals来创建一个显示当前变量的窗口。默认是新创建一个窗口来显示,但是我们可以在命令前加上 belowright来指定在右侧继续分屏,它会在右下角新建窗口来显示变量。需要查看其它窗口可以对应传入不同的参数,例如传入 breakpoints来显示所有断点信息。传入的参数就是gdb中接收的对应参数。有了这些基础我们就可以对其进行配置了,我们要实现的目标就是当gdb成功加载的时候自动加载这些窗口。在vim中要实现自动化我们目前知道有两种方式,第一种使用自动命令,第二种使用插件配置中提供的回调函数。遗憾的是在这个插件中我没有找到回调函数,因此我们只能采用自动命令这种方法。根据官方的文档,我们主要使用这么两个事件——NvimGdbStart和 NvimGdbCleanup。它们一个是成功加载gdb的时候触发,一个是关闭gdb会话的时候触发。vim.cmd([[augroup GdbSession autocmd! autocmd User NvimGdbStart :lua StartGdbSession() autocmd User NvimGdbCleanup :lua EndGdbSession() augroup END]])在 StartGdbSession 函数中写入如下代码来完成窗口的配置StartGdbSession = function() vim.api.nvim_command(":belowright GdbCreateWatch backtrace") vim.api.nvim_command(":wincmd h") vim.api.nvim_command(":belowright GdbCreateWatch info locals") vim.api.nvim_command(":set wrap") vim.api.nvim_command(":wincmd k") end不知道各位小伙伴能不能理解这段代码是如何在分屏的。首先启动gdb的时候会将整个屏幕纵向分为两个部分,左侧为 code右侧为 repl窗口接着我们执行 :belowright GdbCreateWatch backtrace 它会在右下方创建一个窗口用来展示调用栈然后执行 :wincmd h来将光标移动到代码窗口上继续执行 :belowright GdbCreateWatch info locals它会在代码窗口的下方新增一个窗口用于显示变量的信息这样就将窗口分为4个部分了,左上部分显示代码,左下部分显示变量信息。右上部分是repl窗口,右下部分显示变量信息。最后我们通过 :set wrap设置窗口中自动换行,不然有些内容显示在一行不容易查看。通过 :wincmd k移动光标到 repl窗口。方便后续调试启动之后他的效果如下最后我们在结束gdb的时候做一些收尾工作,关闭我们创建的窗口EndGdbSession = function() vim.api.nvim_command(":bdelete! backtrace info locals") end这里我是根据buffer的名称来进行删除。最后的效果如下到此我们已经介绍了关于dap 的所有配置,至于其他语言相信各位小伙伴根据官方给出的示例可以独立完成配置,这里就不一一介绍了。
2022年12月30日
12 阅读
0 评论
0 点赞
2022-11-20
换工作有感
最近很长一段时间没有更新博客,更新关于vim相关的操作,主要是最近在忙于换工作的事情。其实本来我也没打算换工作的,主要是最近公司的一些骚操作让我觉得心里很不爽,所以一怒之下提出离职。
2022年11月20日
11 阅读
1 评论
1 点赞
2022-11-18
从零开始配置vim(29)——DAP 配置
首先给大家说一声抱歉,前段时间一直在忙换工作的事,包括但不限于交接、背面试题准备面试。好在最终找到了工作,也顺利入职了。期间也有朋友在催更,在这里我对关注本系列的朋友表示感谢。多的就不说了,我们正式进入vim 的配置吧上一节通过配置 python 的调试环境,我们大概了解了配置 dap 的基本步骤。首先需要一个 dap 的客户端负责在编辑器上显示各种调试信息,并且与用户进行交互。然后需要一个服务端,与客户端通信并完成调试的实际步骤。然后需要配置两个东西, dap.adapters 用来配置如何启动调试器,dap.configurations用来配置如何将当前项目加载到调试器上。本篇我们进一步配置 dap。让它变得更好用,并且介绍编译型语言(C/C++)调试的配置。优化界面回顾一下上一篇中在演示图片里面看到的效果。默认界面在断点位置以 B 来标识,当前运行的代码以 -> 来标识。看起来不那么的直观,我们先对它进行优化,我们采用 Visual Code 的调试图标来进行标识我们采用以下代码进行配置local dap_breakpoint_color = { breakpoint = { ctermbg=0, fg='#993939', bg='#31353f', }, logpoing = { ctermbg=0, fg='#61afef', bg='#31353f', }, stopped = { ctermbg=0, fg='#98c379', bg='#31353f' }, } vim.api.nvim_set_hl(0, 'DapBreakpoint', dap_breakpoint_color.breakpoint) vim.api.nvim_set_hl(0, 'DapLogPoint', dap_breakpoint_color.logpoing) vim.api.nvim_set_hl(0, 'DapStopped', dap_breakpoint_color.stopped) local dap_breakpoint = { error = { text = "", texthl = "DapBreakpoint", linehl = "DapBreakpoint", numhl = "DapBreakpoint", }, condition = { text = 'ﳁ', texthl = 'DapBreakpoint', linehl = 'DapBreakpoint', numhl = 'DapBreakpoint', }, rejected = { text = "", texthl = "DapBreakpint", linehl = "DapBreakpoint", numhl = "DapBreakpoint", }, logpoint = { text = '', texthl = 'DapLogPoint', linehl = 'DapLogPoint', numhl = 'DapLogPoint', }, stopped = { text = '', texthl = 'DapStopped', linehl = 'DapStopped', numhl = 'DapStopped', }, } vim.fn.sign_define('DapBreakpoint', dap_breakpoint.error) vim.fn.sign_define('DapBreakpointCondition', dap_breakpoint.condition) vim.fn.sign_define('DapBreakpointRejected', dap_breakpoint.rejected) vim.fn.sign_define('DapLogPoint', dap_breakpoint.logpoint) vim.fn.sign_define('DapStopped', dap_breakpoint.stopped)上面的代码主要配置了显示的颜色和图标。最终调试的效果如下图所示然后我们需要提供一个可用的界面用来显示调试过程中的各种信息,包括变量值和调用栈。完成这个工作的是插件 nvim-dap-ui 。我们使用如下的代码进行安装use { "rcarriga/nvim-dap-ui", requires = {"mfussenegger/nvim-dap"} }这个插件里面包装了很多调试相关的窗口,例如变量监控、调用栈等等。我们可以对他进行配置,让这些窗口元素出现在我们希望它出现的位置。为了加载这个插件我们还是按照之前的惯例,为它准备一个单独的配置文件,并且加载它。local dapui = require("dapui") dapui.setup({})我们可以使用该插件中的函数 toggle() 开打开或者关闭这些调试窗口。最终的效果就像这样每次都输入这个函数来打开和关闭调试窗口比较麻烦,因此我们这里可以使用以下代码来实现自动加载和关闭local dapui = require("dapui") dapui.setup({}) local dap = require("dap") dap.listeners.after.event_initialized["dapui_config"] = function() dapui.open({}) end dap.listeners.before.event_terminated["dapui_config"] = function() dapui.close({}) end dap.listeners.before.event_exited["dapui_config"] = function() dapui.close({}) end这段代码在 dap 的事件中注册了几个回调函数,当对应的事件发生时会调用对应的函数,我们在 dap 的调试启动时打开调试窗口,在结束时关闭调试窗口最后关于界面方面的优化再来推荐一个插件——nvim-dap-virtual-text 它的作用是在调试过程中,在变量附近事实显示变量的值。我们可以在 dap-ui 的配置文件中对他进行配置require("nvim-dap-virtual-text").setup({ enabled = true, enable_commands = true, highlight_changed_variables = true, highlight_new_as_changed = false, show_stop_reason = true, commented = false, only_first_definition = true, all_references = false, filter_references_pattern = '<module', virt_text_pos = 'eol', all_frames = false, virt_lines = false, virt_text_win_col = nil })上述的配置是官方给出的,我原封不动的复制过来了。它的效果如下图所示:配置c++基础调试环境终于到了本文最重要的环节了,就是配置 c/c++ 的调试环境,上一篇我们讲解了 Python 的配置,它代表了脚本类解释型语言的调试配置,C/C++ 代表了编译型语言的调试配置。针对 C/C++ 的调试我们选用 cpptools 作为 dap 的服务端。首先通过 MasonInstall cpptools 来下载安装它,也可以通过 :Mason 命令在图形化的界面上进行安装。然后我们还是按照之前的顺序来对他进行配置,首先配置它的加载方式local dap = require("dap") dap.adapters.cppdbg = { id = "cppdbg", type = 'executable', command = "~/.local/share/nvim/mason/bin/OpenDebugAD7", }这里我们设置它以 executable 的方式启动(在客户端调试时启动)。然后指定可执行程序的路径,如果这里报找不到 OpenDebugAD7 这种错误,可以将 ~ 改为 /home/user 这样的具体目录。然后我们配置一下客户端与服务器通信相关的内容dap.configurations.cpp = { { name = "Launch file", type = "cppdbg", request = "launch", program = function() return vim.fn.input("Path to executable: ", vim.fn.getcwd() .. "/", "file") end, cwd = "${workspaceFolder}", stopAtEntry = true, }, } dap.configurations.c = dap.configurations.cpp最后我们通过一个 dap.configurations.c= dap.configurations.cpp 让c++和 c使用同一个配置。因为 C/C++ 是编译运行的,在调试的时候其实调试的是它生成的可执行程序,所以这里每次在调试的时候需要手工指定要调试的可执行程序。最后别忘了在 ftplugin/cpp.lua 中加载它另外需要注意,因为可执行程序运行时是不依赖源代码的,但是调试的时候想让调试器能够准确的知道当前在源码的位置并且能够显示当前变量的值,这个时候需要在可执行程序中打包符号表,对于linux 的 C/C++ 程序来说,只需要在编译的时候给gcc/g++ 传递 -s 参数即可。我们写一个简单的 C程序来进行实验#include <stdio.h> int main (int argc, char *argv[]) { printf("hello world\n"); for (size_t i = 0; i < 10; i++) { printf("i = %ld\n", i); } return 0; }注意: 这里我们使用的调试器仍然是gdb, cpptools 只是在上层进行了一层封装。因此这里能调试的前提是安装了gdb 调试器到此我们将关于 dap 调试的部分都基本介绍完了。其实 dap 也并没有想象中那么难,目前从安装到配置使用,都有大量的插件来方便我们使用,而且官网上基本都有配置的介绍,没有特殊需求只需要将标准配置原样拷贝粘贴即可。下一篇我们将补充一些关于 dap 的其他内容,并介绍 neovim + gdb 的组合,敬请期待!
2022年11月18日
9 阅读
0 评论
0 点赞
2022-10-24
从零开始配置vim(28)——代码的编译、运行与调试
在前面几个章节,我们逐渐为 Vim 配置了语法高亮、代码的跳转和自动补全功能。现在的 Vim 已经可以作为代码编辑器来使用了。但是想将它作为日常发开的主力编辑器来用还需要很长一段路要走,其中一个就是要为它配置代码的一键编译与运行功能。这里我们仍然以 C 和 Python 为例。一个是需要编译运行的一个是直接就可以运行的,这两个语言应该能代表大多数语言的情况。自动运行C 语言的配置在之前 vim 入门的一系列教程中我们介绍过 vim 自带 make 命令的运行机制以及如何进行自定义。对于其他语言要实现这个自动编译运行的效果我们核心的操作就是在修改 make 命令。而 C/C++ 本身采用 make 命令来进行编译和运行,所以这里 C/C++ 我们直接采用 vim 自带的 :make 命令在之前 vim 入门的一系列教程中我们介绍过 vim 自带 make 命令的运行机制以及如何进行自定义。对于其他语言要实现这个自动编译运行的效果我们核心的操作就是在修改 make 命令。而 C/C++ 本身采用 make 命令来进行编译和运行,所以这里 C/C++ 我们直接采用 vim 自带的 :make 命令我们先创建一个 C 的工程。让后使用上一节的生成 hello world 的代码片段生成一个基本的程序。然后提供一个供 :make 命令使用的 Makefile 文件main.out: main.o gcc main.o -o main.out main.o: main.c gcc -c main.c clean: rm -rf *.o *.out run: ./main.out然后我们执行 make 用来编译。如果出错了,可以使用 quickfix 相关命令跳转到对应位置。我们一般的流程是 :make 进行编译,然后使用 :make run 来进行运行。把命令搞清楚了,下面就考虑如何加快这个流程,做到一键编译运行。我们的思路还是绑定快捷键。每种语言虽然定义相同的快捷键但是运行的命令不同,我们需要根据不同的语言类型绑定对应的命令。这个时候最好的办法就是在 filetype 的机制上完成绑定的操作。我们在 lua/lsp/cpp.lua 中绑定快捷键。local on_attach = function(client, bufnr) lsp_set_keymap.set_keymap(bufnr) -- 编译 vim.api.nvim_buf_set_keymap(bufnr, "n", "<F7>", "<cmd>make<CR>", {silent = true, noremap = true}) -- 编译运行 vim.api.nvim_buf_set_keymap(bufnr, "n", "<F5>", "<cmd>make run<CR>", {silent = true, noremap = true}) end到此我们关于 C/C++ 的配置就完成了。可能显的有些简单但是已经初步可用了,小伙伴可以根据自己的需求来进一步修改这个配置。使用这个配置的前提是 C/C++ 的工程中有已经定义好的 Makefile 文件Python的配置之前我们在讲解命令的模式的提到过可以使用 % 来代表当前 buffer 所对应的文件。所以 python 的配置就比较简单了。因为 Python 不需要编译,所以这里直接绑定 <F5> 来运行vim.api.nvim_buf_set_keymap(bufnr, "n", "<F5>", "<cmd>!python %<CR>", {silent = true, noremap = true})dap 配置我们经常看到有人配置 neovim 或者 vim 的时候会介绍到 dap ,那么什么是 dap 呢? dap 的全称是 Debug Adapter Protocol 从名称上看它又是一个协议。它为多种调试器提供了一层统一的适配抽象层。有点类似于前面的介绍的 lsp。只要在适配层提供接口的实现,那么在客户端,也就是代码编辑器这端可以不做任何修改的集成不同调试联想到 lsp 的配置,我们配置dap 首先需要的是有一个 dap 的客户端,用来向调试器发送各种命令,例如下断点、显示变量名等等。另外想要能够调试也需要有具体的调试器,用来接收处理这些命令。现在思路有了,我们 这里先以 Python 为例来介绍 dap 的基本配置。首先是需要一个客户端,用于通过 neovim下发各种调试命令并实时显示调试信息。截止到 0.7 版本 NeoVim 并没有在内部集成 dap 客户端的功能,需要我们单独安装相关插件来实现这部分的功能。这里我们使用的客户端是 nvim-dap 插件。我们先使用 use {'mfussenegger/nvim-dap'} 来安装它。接着我们来定义一下相关的快捷键,这里我喜欢使用 Visual Studio 的快捷键。各位小伙伴可以自行选择自己喜欢的快捷键。这里我希望在插入模式和选择中也可以使用这些快捷键,由于 vim.api.nvim_set_keymap 函数第一个参数只能有一个模式字符串,如果采用这个函数来定义快捷键,这里同样的代码我要写三次,为了简化代码,这里介绍一个新的函数 vim.keymap.set。它与 vim.api.nvim_set_keymap 函数支持的参数相同,只是它第一个表示模式的参数可以支持用字典来一次绑定到多个模式中。这样就简化了绑定快捷键的代码量。vim.keymap.set({"i", "n", "v"}, "<F5>", "<cmd>lua require'dap'.continue()<CR>", {silent = true, noremap = true, buffer = bufnr}) vim.keymap.set({"i", "n", "v"}, "<F10>", "<cmd>lua require'dap'.step_over()<CR>", {silent = true, noremap = true, buffer = bufnr}) vim.keymap.set({"i", "n", "v"}, "<F11>", "<cmd>lua require'dap'.step_into()<CR>", {silent = true, noremap = true, buffer = bufnr}) vim.keymap.set({"i", "n", "v"}, "<F12>", "<cmd>lua require'dap'.step_over()<CR>", {silent = true, noremap = true, buffer = bufnr}) vim.keymap.set({"i", "n", "v"}, "<F9>", "<cmd>lua require'dap'.toggle_breakpoint()<CR>", {silent = true, noremap = true, buffer = bufnr})这个函数是 0.7 以后的版本引进的,如果你的是0.7之前的版本,还是老老实实的多写几遍。另外我们这里绑定了 <F5> 快捷键,因此之前我们在 Python 中,绑定的直接运行的 <F5> 键的代码需要注释一下。我们想要真正实现调试,还需要配合调试器使用。前面说 dap 只是一层协议,需要客户端服务器按照这一层协议来实现相关功能,某些调试器可能自身支持这个协议,而某些可能不支持,这样就需要额外的配置来使调试器也能支持该协议。下面我们以 Python 为例先把整个调试环境搭建起来,先跑起来再说Lsp 在安装 Server 的时候有 nvim-lsp-installer 这样的插件来专门安装 LSP server 的,那么 dap 有没有类似的插件来安装 dap 调试器相关的服务呢?有!我们使用 mason 来管理 dap 的调试器。use { "williamboman/mason.nvim" }当初我推荐过 nvim-lsp-installer 插件作为下载、管理 lsp server 的工具。后来只是知道作者发布了新的管理工具,因为比较新怕出问题就没怎么关注,后来有好多小伙伴在评论区推荐,我仔细看了一下发现它已经支持 dap 服务的管理了。那还是使用它吧 ^_^。我们可以使用 Mason 打开一个带界面的 Lsp 和 DAP 的服务管理窗口,可以使用 数字键在上面进行跳转,找到想要的服务之后直接使用 i 来安装也可以使用 MasonInstall 来安装想要的服务。我们先在插件配置中删除与 nvim-lsp-installer 相关的配置,包括 packer 中对它的引用和 plugins-config 目录中的配置。下一步就是配置 dap 的客户端与 服务端的联动,这需要配置 nvim-dap 插件,根据官方的描述我们主要配置两个部分,第一个部分叫做适配器,主要配置我们加载哪个调试器,以及如何加载调试器。这一步需要提供如下的配置框架local dap = require('dap') dap.adapters.language = { }language 是具体的调试器例如 debugpy 这里的 language 就是 python了。既然与语言相关,我们自然的想到要用 ftplugin目录。为了方便管理,这里与 lsp 配置的组织形式类似,我们将所有关于 dap 的配置都放到 lua/dap目录中。并且按语言名称来命名,例如关于 python 的 dap 配置我们放到 lua/dap/python.lua 中。然后在 ftplugin/python.lua 中加载这个配置文件即。require("dap/python")然后在 python.lua 文件中写入以下配置local dap = require('dap') dap.adapters.python = { type = 'executable'; command = '/usr/bin/python3.8'; args = { '-m', 'debugpy.adapter' }; }其中各个参数的含义如下:type : 表示启动调试器的方式, executable 表示由客户端自行启动调试器; server 表示 调试器已经单独启动了,后续客户端只需要将调试请求发送到服务器即可。command: 表示启动调试器的命令args: 表示启动调试器的命令行参数由于 python 调试工具 debugpy 是一个 Python 的第三方模块,因此这里我们使用 python -m debugpy.adapter 来启动这个调试器。接着我们需要针对语言来配置如何进行调试。它的配置都放在一个名为 dap.configurations.language 的 字典中。language 代表的是当前文件的文件类型名, 所以针对 Python 来说这里需要填写的是 dap.configurations.python。它的配置如下所示:dap.configurations.python = { { type = "python"; request = "launch"; name = "launch file"; program = "${file}"; pythonPath = function () return "/usr/bin/python3.8" end }, }各参数的含义如下:name : 是一个字符串它表示当前配置的名称,你可以理解为一个idtype: 使用哪个调试器,跟我们之前配置的 dap.adapters相关request:调试的方式,支持 attach 附加到一个已有的进程或者 launch 启动一个新进程。由于在上一步我们指定由客户端来启动调试器,因此这里应该选择 launch 来启动一个新调试进程program: 需要调试的代码, ${file} 表示当前 buffer 所对应文件pythonPath: 执行该文件需要使用的 python 解析器路径这样我们在某一个打开的文件上按下 <F5> 的时候,它会通过 pythonPath 指定的解析器来执行脚本,并且会按照配置中 request 指定的方式来打开一个新的调试器进程。并且将对应的调试命令发送到调试器完成调试工作。好了,到此为止我们配置了最基本、最简单的dap 调试。现在只是有一个勉强能用的调试工具,距离好用还差的很远,下一篇里面我们首先会对 dap 功能进行增强,美化,并讨论如何针对 C/C++ 这种编译型的语言进行调试。最后感谢各位给我提意见的小伙伴,谢谢大家!
2022年10月24日
6 阅读
0 评论
0 点赞
2022-10-18
从零开始配置vim(27)——代码片段
我们之前介绍过缩写相关的内容,缩写是可以自动帮我们将缩写的单词展开成一段完整的话。但是代码本身是结构话的,仅仅使用缩写来配置是无法完成自动生成代码这个步骤的。好在我们大量的插件来进行配置。本篇我们将要来讨论如何使用相关插件来完成代码片段自动完成的功能vsnip 插件我们之前在配置自动补全的时候已经下载了 vim-vsnip这个插件。vsnip是一个非常强大的插件,它支持我们使用与 VS Code 类似的方式来扩展定义我们自己的代码片段,同时它也内置了不同语言版本的代码片段。在之前的配置中我们还加了另外一个 friendly-snippets 。它提供了丰富的已定义好的可以直接使用的代码片段,加快了我们的编码效率。在前面介绍补全的时候我们已经安装并配置了它们。它们的效果如下图所示:自定义代码片段虽说这些插件预定义了大量的代码片段。但是他们都是通用型的代码片段,总有那么些时候无法满足我们的需求。一个明显的例子就是不同的公司有不同的代码和注释的风格。这个时候就需要我们自定义了。本篇也准备将重点放在如何自定义代码片段上。如果小伙伴们已经有了在 vscode 上自定义代码片段的经历,那么请跳过本篇以节省各位的时间。入门下面我们以 C 为例来说明如何自定义代码片段来满足我们的需求。其他语言只是填入的内容不同,在定义上并没有什么大的差别。这里我采用 C 语言一个原因是静态类型的语言更方便的演示其中的各项功能,另一个原因就是我对C/C++ 比较熟悉首先我们要找到代码片段的配置文件所在位置,这个位置保存在变量 g:vsnip_snippet_dir 中。我们可以通过 :echo g:vsnip_snippets_dir 找到它。当然也有更简单的办法,我们可以执行 :VsnipOpen 来打开该语言对应的配置文件,如果该文件不存在,命令将创建一个以对应语言名称命名的 json 文件,例如这里它会创建一个 c.json 的文件这里我们先来写一个最简单的代码片段——自动生成一个 Hello World 的 C 程序"example": { "prefix": ["hello"], "body": [ "#include <stdio.h>", "int main(int argc, char* argv[])", "{", "\tprintf(\"hello world\");", "\treturn 0;", "}" ], "description": "create a hello world" }我们来试试效果,输入 hello 将自动生成一段代码现在我们来看看这些字段都代表什么含义example:它代表该代码段的名称,也可以认为它是一个id,用于标识一个代码片段prefix:用于触发该代码片段,这里也就是我们输入 hello将会触发补全,当选择对应项的时候会调用该代码片段进行补全body:补全时自动生成的代码。它可以是一个字符串或者字符数组。虽然它本身也支持 \r\n来进行换行。但是我更倾向于使用字符数组的形式,每一行是数组中的一个字符串。description :描述信息使用占位符如果我们仅仅只能生成像上述 hello world 那样给写死的代码的话,那么它也没什么太大的用处。有时候我们的代码需要生成一个模板,关键地方应该由我们自己进行填充。这个时候可以用到占位符,例如我们使用下面的来生成一个函数"ifunc": { "prefix": ["ifunc"], "body": [ "int $1()", "{", "\treturn 0;", "}" ], "description": "create a function return int" } }占位符内容可选我们现在已经可以生成函数了,但是有一个问题摆在我们的面前。函数的返回类型多种多样,如果我们每一个类型都定义一个片段,例如 返回 int 的定义一个 ifunc,返回 float 的定义一个 ffunc。显得多此一举了,代码的重复度太高了。这个时候我们我们给占位符一些预选项以供我们选择。它的语法格式如下${1|sel1, sel2, sel3,...|}前面的1代表是第一个占位符。如果是后面的占位符需要提供选项,那么就可以依次类推例如我们将上述生成函数的代码片段改为"func": { "prefix": ["func"], "body": [ "${1|int,float,double,char,short,*|} $2()", "{", "\treturn $3;", "}" ], "description": "create a function" } }占位符间跳转生成函数的代码片段中有3个占位符,其中第一个是可以选的,第二个第三个需要我们手动填写。按照习惯,我们一般使用 <Tab> 键来进行跳转,但是这个时候我们发现它没有用。vsip 有自己的命令来跳转到占位符,因此为了保持使用习惯不变,我们需要定义快捷键vim.cmd[[imap <expr> <Tab> vsnip#jumpable(1) ? '<Plug>(vsnip-jump-next)' : '<Tab>']] vim.cmd[[smap <expr> <Tab> vsnip#jumpable(1) ? '<Plug>(vsnip-jump-next)' : '<Tab>']] vim.cmd[[imap <expr> <S-Tab> vsnip#jumpable(-1) ? '<Plug>(vsnip-jump-prev)' : '<S-Tab>']] vim.cmd[[smap <expr> <S-Tab> vsnip#jumpable(-1) ? '<Plug>(vsnip-jump-prev)' : '<S-Tab>']]由于该插件是使用 vimscript 脚本写的,它还没有提供 lua 的接口,因此这里我也就使用 vimscript 的方式来定义快捷键。使用变量使用语法 $name 或者 ${name: default} 可以插入一个变量。如果未设置变量,则会插入其默认值或空字符串。当变量未知(未定义其名称)时,会将插入的变量名称转换为占位符。这里的变量一般是环境变量或者是 vim 自带的一些变量。我们直接拿来用但是在不同的环境下得到的结果是不一样的。我们可以使用变量来丰富一些信息。例如我们使用下面的代码片段来生成注释"doc-header": { "prefix": ["doc-header"], "body": [ "/**", "* @file ${TM_FILENAME}", "* @bref ${1}", "* @details ${2}", "* @date ${CURRENT_YEAR}/${CURRENT_MONTH}/${CURRENT_DATE}", "* @commit history:", "* \t v${3}: ${4}", "*/${0}" ], "description": "create a doc header" }这样我们就生成了一个 .cpp 文件开头的注释了。其中用到了 TM_FILENAME 来表示文件名,使用 CURRENT_YEAR CURRENT_MONTH CURRENT_DATE 来分别获取到年月日。它的效果如下:这里的 ${0} 一般用来截断 Tab键的跳转,也就说遇到 ${0} 之后 <Tab> 键就不再起到跳转到下一个占位符位置这个作用了。具体有哪些变量可以使用,可以参考 visual studio code 官方给出的文档本篇主要谈论了该如何定义自己的代码片段。如果想要更完整的内容可以参考 Visual Studio Code 官方的文档。我们也可以从Visual Studio Code 相关代码片段中 Copy 部分来进行使用。
2022年10月18日
7 阅读
0 评论
0 点赞
2022-10-14
从零开始配置vim(26)——LSP UI 美化
之前我们通过几个实例演示如何配置其他语言的lsp服务,相信各位小伙伴碰到其他的编程语言也能熟练的配置它对应的lsp服务。本篇讲作为一个补充,我们来优化一下LSP 相关的显示配置 UI原始的 lsp 显示有点素,我们使用插件对它进行一些美化,这里使用插件 lspsaga.nvim 。使用如下的代码进行安装use{"glepnir/lspsaga.nvim"}然后我们新建一个 plugin-config/lspsaga.lua 对它进行配置local saga = require('lspsaga') saga.init_lsp_saga()该插件对 NeoVim 原生 LSP 显示做了一些更改,并提供了一些方便的命令来实现LSP 相关的功能。我们将它对应的功能绑定到快捷键上替换原有的 LSP 对应的快捷键lsp_keybinds.set_keymap = function (bufnr) print("set lsp keymap") -- 跳转到声明 vim.api.nvim_buf_set_keymap(bufnr, "n", "gd", "<cmd>Lspsaga peek_definition<CR>", {silent = true, noremap = true}) -- 跳转到定义 vim.api.nvim_buf_set_keymap(bufnr, "n", "gD", "<cmd>lua vim.lsp.buf.definition()<CR>", {silent = true, noremap = true}) -- 显示注释文档 vim.api.nvim_buf_set_keymap(bufnr, "n", "gh", "<cmd>Lspsaga lsp_finder<CR>", {silent = true, noremap = true}) -- 跳转到实现 vim.api.nvim_buf_set_keymap(bufnr, "n", "gi", "<cmd>lua vim.lsp.buf.implementation()<CR>", {silent = true, noremap = true}) -- 跳转到引用位置 vim.api.nvim_buf_set_keymap(bufnr, "n", "gr", "<cmd>Lspsaga rename<CR>", {silent = true, noremap = true}) -- 以浮窗形式显示错误 vim.api.nvim_buf_set_keymap(bufnr, "n", "go", "<cmd>lua vim.diagnostic.open_float()<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "n", "gp", "<cmd>lua vim.diagnostic.goto_prev()<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "n", "gn", "<cmd>lua vim.diagnostic.goto_next()<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>cd", "<cmd>Lspsaga show_cursor_diagnostics<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>cd", "<cmd>Lspsaga show_line_diagnostics<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "n", "<leader>ca", "<cmd>Lspsaga code_action<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "v", "<leader>ca", "<cmd>Lspsaga code_action<CR>", {silent = true, noremap = true}) end我们可以对比一下原生的 lsp 界面和 lspsaga 界面,我这里使用 gh 这个命令进行对比我们可以看到它能显示更丰富的信息,而且我们可以根据显示快速跳转到对应位置。对于我们查阅代码也是一个大的提升而且它还提供 code action 相关的功能。例如上面的截图中它在出现错误的一行代码的行号前以小灯泡的形式进行标记,体验类似与 VS Code。我们将光标放在对应位置,使用绑定的快捷键 <leader>ca 。它会显示出对应的修复方案当然它的功能还不止于此,各位小伙伴可以去对应的官网上阅读相关文档,针对自己的需求进行进一步的配置显示符号表我们可以使用插件 symbols-outline 在窗口右侧显示当前 buffer 中出现的类、方法等符号,方便我们快速跳转到想去的地方。该插件仅支持 NeoVim7,0 以上的版本。使用下面的代码进行安装use {'simrat39/symbols-outline.nvim'}还是额外的给它创建一个文件保存它的配置require("symbols-outline").setup()配置完成之后我们可以使用 :SymbolsOutline 命令来控制窗口的打开和关闭,为了方便我们可以绑定到一个快捷上配置完成之后我们可以使用 :SymbolsOutline 命令来控制窗口的打开和关闭,为了方便我们可以绑定到一个快捷上vim.api.nvim_set_keymap("n", "<leader>so", "<cmd>SymbolsOutline<CR>", {silent = true, noremap = true})最终的效果如下图所示本章我们对之前的LSP 配置进行了一定程度的优化,优化了使用的体验。截止到当前文章我们已经完成了代码的高亮、跳转、语法分析、代码补全、错误提示以及修复建议的相关配置。关于LSP的相关配置已经结束了,下一篇我们将要讨论如何使用代码片段进一步减少我们编码时输入的字符数。请各位敬请期待
2022年10月14日
8 阅读
0 评论
0 点赞
2022-10-09
从零开始配置vim(25)——关于 c++ python 的配置
从9月份到国庆这段时间,因为得了女儿,于是回老家帮忙料理家事以及陪伴老婆和女儿。一时之间无暇顾及该系列教程的更新。等我回来的时候发现很多小伙伴私信我催更。在这里向支持本人这一拙劣教程的各位小伙伴表示真诚的感谢。言归正传,让我们开始吧之前我们根据lua语言配置了基于lsp的代码高亮、自动跳转、自动补全等等功能,那个时候我们安装了很多插件,像 nvim-lspconfig、nvim-lsp-installernvim-cmp等等,每个插件都在干嘛,虽然我们配置好了 lua相关的内容,但是可能仍然有小伙伴有疑问,碰到其他语言该如何配置,是不是要重新下载对应的插件呢?为了解答这些问题,这篇文章我们将要来根据 c++和 python的日常习惯来进行配置,给大家演示一下在上述内容都配置完成之后面对其他语言我们该如何进行处理安装配置 c++ 相关的lsp服务关于c++ 的服务,我们根据 nvim-lsp-installer 官方给出的表格中显示它可以使用 ccls 和 clang,这里我们以 ccls 作为示例进行讲解。首先通过命令安装 :LspInstall ccls接着我们新建一个 ftplugin/c.lua 和 ftplugin/cpp.lua 来配置 c/c++ 。不过他们两个采用相同的配置,我们暂时将一份配置复制两遍require("lsp/cpp")他们的作用只有一个,那就是加载 lsp/cpp 这个文件,我们将他们的配置放到一个文件中然后我们再在 lua/lsp/cpp.lua 文件中加入以下内容用于启动 lsp服务端local lspconfig = require('lspconfig') lspconfig.ccls.setup { init_options = { cache = { directory = ".ccls-cache"; }; } }我们进入一个 .c/.cpp 文件发现已经加载了 ccls 了。如果没有加载可以使用 :LspStart 命令手工加载或者使用 LspInfo 查看是否有问题现在我们已经可以看到lsp服务给出的提示了修改之前的配置我们在第22篇文章中给出了基于 lsp 的 lua 的配置,主要是使用 lsp 服务端的配置和对应的跳转之类的快捷键配置,我们将它放到了 lsp/lua.lua 目录下了。但是一想想我们使用 c++、Python 或者其他什么语言的时候,这些快捷键应该是不会修改的,这个时候自然就想到了要重用快捷键了。所以来配置之前的第一件事就是想办法重用这些快捷键。这个时候我们想到的办法就是将之前定义的快捷键封装成函数,然后在 on_attach 的回调函数中调用该函数。我们将那些快捷键定义放到 lua/keybindings.lua 中local lsp_keybinds = {} lsp_keybinds.set_keymap = function (bufnr) -- 跳转到声明 vim.api.nvim_buf_set_keymap(bufnr, "n", "gd", "<cmd>lua vim.lsp.buf.declaration()<CR>", {silent = true, noremap = true}) -- 跳转到定义 vim.api.nvim_buf_set_keymap(bufnr, "n", "gD", "<cmd>lua vim.lsp.buf.definition()<CR>", {silent = true, noremap = true}) -- 显示注释文档 vim.api.nvim_buf_set_keymap(bufnr, "n", "gh", "<cmd>lua vim.lsp.buf.hover()<CR>", {silent = true, noremap = true}) -- 跳转到实现 vim.api.nvim_buf_set_keymap(bufnr, "n", "gi", "<cmd>lua vim.lsp.buf.implementation()<CR>", {silent = true, noremap = true}) -- 跳转到引用位置 vim.api.nvim_buf_set_keymap(bufnr, "n", "gr", "<cmd>lua vim.lsp.buf.references()<CR>", {silent = true, noremap = true}) -- 以浮窗形式显示错误 vim.api.nvim_buf_set_keymap(bufnr, "n", "go", "<cmd>lua vim.diagnostic.open_float()<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "n", "gp", "<cmd>lua vim.diagnostic.goto_prev()<CR>", {silent = true, noremap = true}) vim.api.nvim_buf_set_keymap(bufnr, "n", "gn", "<cmd>lua vim.diagnostic.goto_next()<CR>", {silent = true, noremap = true}) end return lsp_keybinds我们将它打包到lsp_keybinds 模块中作为 keybinds.lua 文件的导出模块。然后在 lsp/lua.lua文件的 on_attach 函数中调用这个函数完成快捷键的配置工作local lsp_set_keymap = require("keybindings") local on_attach = function(_, bufnr) lsp_set_keymap.set_keymap(bufnr) end现在我们就完成了 c/c++ 相关的配置尝试一下绑定的那些快捷键,发现它可以正常进行跳转是不是很简单了,我们没有安装任何的插件,只是安装了 c++ lsp 的服务端。剩下的配置依然延续之前的就好了python 相关配置我们再来以 python 的配置作为例子来讲lsp的配置。python 的服务端我们采用 pyright 。还有一个方法可以安装 lsp 服务。我们可以在命令模式中输入 :LspInstallInfo 查看当前已安装的 lsp 服务。下面会列出一堆的未安装的 lsp 服务。这个时候我们可以将光标移动到某个服务上,按下 i 来安装该服务。这里我提前已经安装好了,所以它显示在 Installed Server 中,没有安装它应该显示在下方的Available Server 列表中。安装完成之后我们还是按照惯例,在 ftplugin 目录下建立一个 python.lua 文件并且在该文件中加载 lua/lsp/python.lua 文件我们在 lua/lsp/python.lua 文件中加入下面的代码local lsp_set_keymap = require("keybindings") local util = require 'lspconfig/util' require('lspconfig').pyright.setup{ on_attach = function(_, bufnr) lsp_set_keymap.set_keymap(bufnr) end, cmd = { "pyright-langserver", "--stdio" }, filetypes = { "python" }, settings = { python = { analysis = { autoSearchPaths = true, diagnosticMode = "workspace", useLibraryCodeForTypes = true, typeCheckingMode = "off" }, }, }, root_dir = function(fname) local root_files = { 'pyproject.toml', 'setup.py', 'setup.cfg', 'requirements.txt', 'Pipfile', 'pyrightconfig.json', } return util.root_pattern(unpack(root_files))(fname) or util.find_git_ancestor(fname) or util.path.dirname(fname) end, }前面的配置与 c++ 的配置类似。主要设置快捷键,这里需要注意的是 root_dir 这里的配置,想要在对应缓冲中启动相应的 lsp 服务,需要buffer处在对应语言的项目中,root_dir 规定在 buffer 所在目录中存在这些文件或者目录时将该目录作为对应项目文件如果我们不加该配置,可能会导致lsp 服务启动失败。如果失败的话我们使用 :LspInfo 来查看失败原因这个根目录设置是可以作用于它下面所有子目录的。这里我根据 python 中常用文件给出了一个列表,各位小伙伴可以根据自己的需求自行添加另外需要注意的一个问题时,pyright 依赖于 node 的 work_threads 模块,该模块从12版本以后才被支持,如果小伙伴的 node 版本低于该版本需要考虑升级 node根据这两个例子,相信各位已经熟悉了该如何配置不同语言的 lsp 服务了。这里面没有什么深奥的代码,也没有什么个性化到只有自己才会用的配置,希望能起到抛砖引玉的作用,后面再碰到什么其他语言小伙伴们应该可以很容易的添加它的lsp服务了。
2022年10月09日
6 阅读
0 评论
1 点赞
1
...
6
7
8
...
32