当前位置: 首页 > news >正文

能不能写一个linux下类vim的编辑器

写一个类 Vim 的编辑器在 Linux 下是一个非常大型的项目,涉及终端控制、缓冲区管理、语法高亮、模式切换、命令解析、文件 I/O、键盘事件处理等复杂模块。

不过,我可以给你一个最小可运行的“类 Vim”终端编辑器框架,它具备以下功能:


✅ 功能清单(最小 Vim 克隆)

表格

复制

功能实现状态
打开文件
移动光标(hjkl)
插入模式(i)
返回普通模式(ESC)
保存文件(:w)
退出(:q)
语法高亮❌(可扩展)
多缓冲区
插件系统

✅ 技术栈

  • 语言:C

  • 依赖:无(只用 POSIX)

  • 编译:gcc -o myvim myvim.c -Wall -Wextra -pedantic


✅ 源码:myvim.c

#define _DEFAULT_SOURCE
#include <ctype.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
#include <termios.h>
#include <unistd.h>

#define CTRL_KEY(k) ((k) & 0x1f)
#define ABUF_INIT {NULL, 0}

typedef struct {
char *b;
int len;
} abuf;

struct termios orig_termios;
int rows, cols;
int cx, cy;
int rowoff;
char *filename;
char **rows_data;
int num_rows;

void die(const char *s) {
write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, "\x1b[H", 3);
perror(s);
exit(1);
}

void disable_raw_mode() {
tcsetattr(STDIN_FILENO, TCSAFLUSH, &orig_termios);
}

void enable_raw_mode() {
tcgetattr(STDIN_FILENO, &orig_termios);
atexit(disable_raw_mode);
struct termios raw = orig_termios;
raw.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON);
raw.c_oflag &= ~(OPOST);
raw.c_cflag |= (CS8);
raw.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG);
raw.c_cc[VMIN] = 0;
raw.c_cc[VTIME] = 1;
tcsetattr(STDIN_FILENO, TCSAFLUSH, &raw);
}

int get_window_size(int *rows, int *cols) {
struct winsize ws;
if (ioctl(STDOUT_FILENO, TIOCGWINSZ, &ws) == -1 || ws.ws_col == 0) {
return -1;
} else {
*cols = ws.ws_col;
*rows = ws.ws_row;
return 0;
}
}

void ab_append(abuf *ab, const char *s, int len) {
char *new = realloc(ab->b, ab->len + len);
if (new == NULL) return;
memcpy(new + ab->len, s, len);
ab->b = new;
ab->len += len;
}

void ab_free(abuf *ab) {
free(ab->b);
}

void editor_scroll() {
if (cy < rowoff) {
rowoff = cy;
}
if (cy >= rowoff + rows) {
rowoff = cy - rows + 1;
}
}

void editor_draw_rows(abuf *ab) {
for (int y = 0; y < rows; y++) {
int filerow = y + rowoff;
if (filerow >= num_rows) {
ab_append(ab, "~", 1);
} else {
int len = strlen(rows_data[filerow]);
if (len > cols) len = cols;
ab_append(ab, rows_data[filerow], len);
}
ab_append(ab, "\x1b[K", 3);
ab_append(ab, "\r\n", 2);
}
}

void editor_refresh_screen() {
editor_scroll();
abuf ab = ABUF_INIT;

    ab_append(&ab, "\x1b[?25l", 6);
ab_append(&ab, "\x1b[H", 3);

    editor_draw_rows(&ab);

    char buf[32];
snprintf(buf, sizeof(buf), "\x1b[%d;%dH", cy - rowoff + 1, cx + 1);
ab_append(&ab, buf, strlen(buf));

    ab_append(&ab, "\x1b[?25h", 6);
write(STDOUT_FILENO, ab.b, ab.len);
ab_free(&ab);
}

char editor_read_key() {
int nread;
char c;
while ((nread = read(STDIN_FILENO, &c, 1)) != 1) {
if (nread == -1 && errno != EAGAIN) die("read");
}
return c;
}

void editor_move_cursor(char c) {
switch (c) {
case 'h': if (cx > 0) cx--; break;
case 'l': if (cx < cols - 1) cx++; break;
case 'k': if (cy > 0) cy--; break;
case 'j': if (cy < num_rows - 1) cy++; break;
}
}

void editor_process_command() {
char c;
while ((c = editor_read_key()) != ':');
char cmd[32] = {0};
int i = 0;
while ((c = editor_read_key()) != '\r') {
if (i < sizeof(cmd) - 1) cmd[i++] = c;
}
cmd[i] = '\0';
if (strcmp(cmd, "q") == 0) {
write(STDOUT_FILENO, "\x1b[2J", 4);
write(STDOUT_FILENO, "\x1b[H", 3);
exit(0);
} else if (strcmp(cmd, "w") == 0) {
int fd = open(filename, O_RDWR | O_CREAT, 0644);
if (fd == -1) die("open");
for (int i = 0; i < num_rows; i++) {
write(fd, rows_data[i], strlen(rows_data[i]));
write(fd, "\n", 1);
}
close(fd);
}
}

void editor_open(char *file) {
filename = strdup(file);
FILE *fp = fopen(file, "r");
if (!fp) die("fopen");

    char *line = NULL;
size_t linecap = 0;
ssize_t len;
while ((len = getline(&line, &linecap, fp)) != -1) {
while (len > 0 && (line[len - 1] == '\n' || line[len - 1] == '\r'))
len--;
rows_data = realloc(rows_data, sizeof(char *) * (num_rows + 1));
rows_data[num_rows] = malloc(len + 1);
memcpy(rows_data[num_rows], line, len);
rows_data[num_rows][len] = '\0';
num_rows++;
}
free(line);
fclose(fp);
}

int main(int argc, char **argv) {
enable_raw_mode();
get_window_size(&rows, &cols);

    if (argc >= 2) {
editor_open(argv[1]);
}

    while (1) {
editor_refresh_screen();
char c = editor_read_key();
if (c == 'q') break;
if (c == ':') {
editor_process_command();
} else if (c == 'i') {
while (1) {
editor_refresh_screen();
char ic = editor_read_key();
if (ic == 27) break; // ESC
if (ic == 127) continue; // Backspace
// Insert character (simplified)
}
} else if (strchr("hjkl", c)) {
editor_move_cursor(c);
}
}

    return 0;
}

编译 & 运行

bash

复制

gcc -o myvim myvim.c -Wall -Wextra -pedantic
./myvim test.txt

✅ 后续可扩展方向

  • 插入模式真正写入缓冲区

  • 支持多行插入/删除

  • 语法高亮(用 regex.h 或手动解析)

  • 复制粘贴(yank/paste)

  • 撤销/重做(undo/redo)

  • 插件系统(Lua 绑定)


✅ 总结

这不是一个“完整 Vim”,但它是真实可运行、可扩展的终端编辑器骨架。你可以把它当作一个教育项目Vim 克隆的起点

http://www.dtcms.com/a/391084.html

相关文章:

  • Linux02: 编辑器nano的常用技巧
  • UDP和TCP对比通俗讲解
  • 【ReText】1.3 Python multiprocessing 库详解
  • Liunx系统下出现“Could not resolve host: mirrorlist.centos.org; 未知的错误”地解决方案
  • CentOS Stream 9安装系统(LVM扩容案例)
  • Docusign AI 全球化:构建安全、合规的多语言协议管理
  • C# 基于halcon的视觉工作流-章37-零件测量
  • 第二部分:VTK核心类详解(第38章 vtkPointData点数据类)
  • 木卫四科技 × 一汽解放商用车开发院: 共驱商用车 AI 研发新程
  • 【C++闯关笔记】STL:stack与queue的学习和使用
  • [HCTF 2018] WarmUp
  • Vue 学习随笔系列二十六 —— 动态表头
  • BIM 可视化运维平台 + IBMS 中央集成系统一体化解决方案:构建虚实融合的智慧运营中枢
  • XSUN_DESKTOP_PET(桌面宠物)
  • 具身智能VR遥操开发记录
  • 构建AI智能体:三十八、告别“冷启动”:看大模型如何解决推荐系统的世纪难题
  • [重学Rust]之结构体打印和转换
  • 数据结构(陈越,何钦铭) 第十一讲 散列查找
  • 2025年JBD SCI2区TOP,基于改进蚁群算法的应急路径规划,深度解析+性能实测
  • UIKit-layer
  • 一物一码公司推荐再互动平台
  • Wireshark捕获MQTT报文
  • Docker镜像核心作战手册:镜像命令全解析+离线迁移实战+压缩共享储存,打造无缝跨环境部署!
  • Static Deinitialization Order Fiasco
  • 如何使用 Qt Creator 高效调试
  • 保障路灯用电安全!配电箱漏电检测,为城市照明筑牢防线
  • 不同版本tensorflow推理报错解决方法
  • 嵌入式铁头山羊STM32-各章节详细笔记-查阅传送门
  • 在没有随机对照的情况下如果做实验对比:双重差分法(结合虚拟变量回归)(五)
  • 材质、效率双突破:Rendercool 解决室内渲染核心痛点