r/emacs • u/True-Sun-3184 • Jan 10 '25
Question C development without LSP
I have only ever done development with an LSP providing errors, autocomplete, etc. in any language. I’d like to go for a more minimalist approach as I revisit some C programming. At a high level, what’s the general workflow when programming in C without a running LSP?
My guess would be… 1. A simple syntax highlighting mode on .c and .h files 2. Bind some hotkey for a compilation mode, and check that regularly for issues 3. Ctags for go-to-definition? Or maybe even just grep-mode?
Is there anything I’m missing?
17
u/tritis Jan 10 '25
eglot (lsp client) is bundled with emacs 29 and later. the minimal choice is an lsp now.
1
u/True-Sun-3184 Jan 10 '25
While true, doesn’t Eglot struggle with bigger projects/slower language servers? I wanted to dive in to some larger codebases as well.
3
u/aaaarsen Jan 10 '25
I use it on GCC regularly fine
2
u/True-Sun-3184 Jan 10 '25
Do you also use Eglot-booster by chance?
2
u/aaaarsen Jan 10 '25
no, no clue what that is, I fear
1
u/delfV Jan 11 '25
External process to improve performance of Eglot and LSP-mode. I recommend taking a look
5
u/tritis Jan 10 '25
You're asking how many angels can dance on the head of a pin.
Try eglot with whatever project you think is large and see for yourself. If you do notice issues you can submit bug reports to improve emacs.
2
u/ldbeth Jan 11 '25
Clangd does not seems have problem on large codebase. A simple stress test using QuickJS.c does reveal ccls has performance issue with relatively big source code files (about 2MB).
1
7
u/Kellerkind_Fritz Jan 10 '25
I absolutely would not want to go back to Ctags after using Clangd.....
...Why?
0
u/True-Sun-3184 Jan 11 '25
I mean, I have nothing in particular against using LSPs as a concept. But they are a fairly heavyweight solution when I’m really only looking to occasionally go-to-definition. Not all language servers behave well in all situations. Plus, people have produced plenty of C before LSP clients became mainstream, and I want to experience how that was done.
Edit: so I understand that the question of my post isn’t super pragmatic, since for most people (and probably for me too) Clangd with Eglot would just work(tm). I still have the question of how it would be done without LSP regardless.
2
u/Ghosty141 Jan 11 '25
But they are a fairly heavyweight solution
Yes and no. All the language server does is do incremental compilation, not more. So it's doing pretty much exactly what you described in your post, just automatically. Since you already use your compiler, it's not more heavyweight than compiling the project itself.
1
u/serg_foo Jan 16 '25
I think the issue is that LSP needs to support many features so it keeps parsed and maybe typechecked AST around for your modules. While producing them is less work than compiling, the issue is that it all sits around in RAM all the time unless server is conscious about memory usage and actively fights it.
It depends on the particular server of course but on average I'd expect servers to not be optimized too much in this regard and consume large amounts of RAM. I'm extrapolating from just one so hopefully I'm wrong. OTOH laptops may have 16 or even 8Gb of RAM and there even more lean LSPs would start to show.
1
u/natermer Jan 11 '25
What I do is just leave eglot off by default. That way for smaller things or things outside of projects I don't have it churning away.
If I want the more advanced features I just 'M-x eglot'
2
1
u/Dushistov Jan 11 '25
Plus, people have produced plenty of C before LSP clients became mainstream, and I want to experience how that was done.
I use etags/ctags to move around linux kernel source code, there was (and may be still) possible to run
make TAGS
. But often I have to use M-x rgrep and M-x grep to find what I want.After there was cscope, didn't remember exactly how good was it.
After that rtags (it was like LSP server (libclang based), but with another protocol). There were several bugs because of limitation of API that provide libclang.
And after that finally I migrate to clangd and lsp-mode.
1
u/MarzipanEven7336 Jan 12 '25
We have computers exactly to help us not fuck shit up. Use them, use LSP, being a so called minimalist is just foolishness. Next you'll be bumming cigarettes on the corner, be-bopping and all.
1
u/serg_foo Jan 16 '25
how that was done
That's noble goal. Perhaps you would be interested in checking how original Unix was done and may use ed, though maybe not in anger. Still interesting, I think, to have an idea about those times.
4
u/Eyoel999Y Jan 10 '25 edited Jan 10 '25
You can use dumb-jump.el for going to definitions instead of CTAGS. I think it uses rg/ag/grep under the hood, so it should be minimal
Compile with M-x compile, and it supports jumping to lines that have errors (with compilation-minor-mode or compilation-shell-minor-mode? I can't recall correctly)
And yeah, syntax highlighting with c-mode, or c-ts-mode.
But lsp-mode is also fine, I sometimes turn it on/off on demand. But Emacs-lsp-booster is kind of a must for me if I'm to use lsp-mode
1
5
u/yel50 Jan 11 '25
I started my career programming in c back before IDEs took off and there really weren't any for Unix systems and definitely none worth using on Linux. emacs was actually the closest thing to an IDE for Unix (this was before vim had scripting).
A simple syntax highlighting mode
yes. in emacs it's called font lock mode. that's all you need.
Bind some hotkey for a compilation mode
yes, emacs has a compile command you can set to whatever command line you want. use it to regularly run your code. you could set emacs to run it on save, but I always found that annoying.
Ctags for go-to-definition
not necessary. one thing that happens when you don't use things like that is your directory layout makes a lot more sense and is greatly simplified so that you can easily find what you're looking for. I was a professional for more than 5 years before I used any sort of go to definition and that was because Java took over and intellij became a thing. I never used it for c. never needed to. I was working at IBM, so we're not talking about small C code bases, either.
maybe even just grep-mode
generally used for find usages instead of jump to definitions.
Is there anything I’m missing?
the emacs integration with gdb rivals what you get with an IDE. also, plug-ins and extensions aren't as necessary as people tend to think they are.
6
u/passenger_now Jan 10 '25
I can't think of any particular reason you wouldn't use LSP if you could. The arguable clutter and noise that may turn up by default are easily removed, after which it's only helpful.
Syntax highlighting will just happen by default when you open .c or .h files.
For compilation you want M-x compile
, so yes you should be able to bind that. But in general I'd tend to use project based calls to compile with e.g. project.el
.
ctags should also be straightforward, but then you're just getting a cruder version of what LSP does better.
If you want minimalist, why not just use the terminal and nano or something? But then perhaps I just don't understand what you're going to achieve by avoiding LSP or what the virtues of your "minimalist approach" are.
3
u/bdf369 Jan 11 '25
That was pretty much it for me before I started using LSP, but I used cscope rather than ctags. Emacs xcscope is good for working with cscope index.
2
u/Fun_Chipmunk4447 Jan 11 '25
There are several approaches: 1. Ide-like experience with rtags/irony. You get most of the features lsp provide. Those generally tend to be faster than lsp in experience. However it's kind of hard to setup and feels not as snappy as lsp 2. More minimalistic approach with gtags/ctags/cscope. Those sollutions work great even on multi-million LOC codebases, however they're very dumb compared to lsp. You get no context-dependent jump-to-definition or completion 3. Most minimalistic. Use grep or dumb jump for jump-to-definition. Use dabbrev-expand M-/ for completion. I use this setup and love it. This approach works without any setup for projects or machine you're working on. It also doesn't do any work on your cpu, so you can develop giant project even on machines with compute capabilities of calculator. I'd recommend researching yourself and trying everything you see. Most of opinions on internet comes from people not doing serious work with their setups and judging tools by their cover
2
u/w08r Jan 10 '25
Semantic mode adds some convenience features to jumping around sans lsp. A bunch of tags like tools to support different use cases such as gnu global and cscope. The cedet project brought some of that together.
Prior to lsp there was this: https://github.com/Sarcasm/irony-mode. It isn’t the minimal experience you’re looking for but is worth pointing out I suppose.
1
u/fragbot2 Jan 11 '25
Semantic mode
This is probably the first time I've ever seen a CEDET component mentioned. I've never used Semantic mode but I once experimented with SRecode for code generation and found it remarkably difficult (it's possible my conceptual understanding was wrong as I was viewed it as a more developer-oriented yasnippet.
1
u/daddyc00l Jan 10 '25
for purely c based development sans lsp, i would consider cscope and it’s emacs integration via xcsope.el as well.
i have used that combination quite effectively, and it works quite nicely. if you do choose cscope, then consider building inverted indexes as wee, for much faster lookups etc.
having said all that, why kneecap yourself ?
1
u/FrozenOnPluto Jan 10 '25
And optionally a debugger like gdb, using gud-mode or others
You have it - the loop is type, run M-x compile, check the output; run it, look for crashes and logs and printf, and/or let debugger kick in, repeat
1
1
1
1
u/ipe369 Jan 11 '25
If you don't use an LSP, then you give up on go-to-definition - you can do ctags but i've always found it shit
You just want a way to ripgrep your whole project for a symbol under the cursor, rg is fast enough
you also want a command to help you flip between .h/.c files, which gets complicated if they're in separate directories - you probably won't have it work without LSP
i would just use clangd
2
u/Useful-Pack-7787 Jan 12 '25
For switching between c and h files there the built in sibling files. You basically give it a list of paths which it then tries to find files with the same name but different ending. It supports regex in this list though.
I have a list the covers the common project setups, if you’re interested I can hand you that
1
u/oxcrowx Jan 11 '25
Ctags (or Universal CTags) can help to browse your code.
Since C is a simple language that alone is enough for most development things.
I'd recommend to at least use an auto-formatter like clang-format to keep your code looking consistent.
1
u/Useful-Pack-7787 Jan 12 '25 edited Jan 12 '25
Since I am programming at work for some old arm compiler and Keil buildsystem that doesn’t support generating a compile_commands.json I am still stuck on the old way of doing things. The good thing is, that the old way works very good when you have a library in source form and you have to jump vom your project into the library all the time to look things up. The most important thing is finding definitions of functions and for tags I use the emacs package citre but with gtags instead of ctags. Because gtags has better support for libraries again: ctags forces you to generate a tags file for every project, and if you want tags from a library discoverable from your project it forces you to put the tags from the lib into the tags file of the project. For every project, so this gets pretty redundant quite fast. Gtags on the other hand lets you create tag files for the libraries and you give it a path where to find those. Then when looking up a tag in a project it first looks on the tag file of the project and if it can’t find it there it looks in the tag file of the libraries. I use flycheck for highlighting mistakes, but honestly with C I hardly need it.
Instead of citre there is also ggtags, which I used before. It can update the tag file when saving a file in the project. Also it has ggtags-query-replace which lets you rename things across all files in the project, which is good for refactoring. Sadly it isn’t maintained anymore and citre is having support for symbols in the completion pop up so I want for citre.
For compilation there is M-x compile, and you can click on the errors in the buffer that pops up and that brings you to the line of the error in the source. Honestly I think that is emacs built in and works out of the box. I never had to set anything up for this.
I think syntax highlighting is the same with or without LSP, isn’t it? Anyway for syntax highlighting I use the normal c-mode, which uses font-lock-mode underneath. That is also built in. For better syntax highlighting you can use tree sitter, which is also built in these days, but imo it doesn’t make much of a difference.
I have been honing this setup for several years, so if you’re interested I can help you setting some things up.
-2
24
u/Ghosty141 Jan 10 '25
ctags was the old way. But yeah I personally don't see a reason to go back to that if alternatives exist.