The absolute minimum is to have access to GLSL compile and link logs here example:
For really hard stuff its best to write your shader as CPU side C++ code and when fully debugged then run it as shader. But for that you would need to write some kind of framework that would emulate shaders. For starters you need 1/2/3/4D
vector and matrix arithmetics from GLSL. It is not that easy to code it but there are also another options like GLM. I am using my template but just to enable vec
datatypes its around 228 KByte of hideous code due to its getters setters for more info look here:
And another absolute minimum is handling input and output. As the fun part is usually located in fragment itst often enough to process only fragment shader this way so you need just 2 nested loops looping through test image calling your shader main
function and setting the pixel according to its result. I usually place #define texture(...)...
to emulate texture access and that is in nutshell all. For example these shaders I would not be possible to debug otherwise:
As was mentioned in comments for simpler shaders you can write them in any C++ IDE to enable syntax highlight. But its a good idea to write/debug the stuff in your target App where you simply add some subwindow where you render the GLSL info logs as opening text file after each compilation is a dull click work.
When I started coding shaders I was tired of such and write my own IDE for shaders. Its buggy and far from perfect but it fulfills my needs. It looks like this:

Link to it is in (edit3) here:
There are some similar tools out there but having my own with source code allows a whole lot of stuff like using targets app CPU side code etc ...
Another very useful thing is to be able to print some variable value from fragment shader. Exactly that I was able to do like this:
you just need to decide correct conditions for entire area of the print (so the value you are printing is the same while printing all its pixels). This help me a lot especially to use 3D textures and geometry encoded in textures for the first time...
However in most cases you can use color output instead of debug prints but having the ability to print actual numbers can save you a lot of time.