That's a good question. Admittedly, my original comment is informed by the general impressions IDEs gave me back then and now, so I'll try to be more specific this time. Of course, I haven't used every existing IDE from mid 90s, so some of them might actually have had these features. If you're aware of any, I'm curious to hear about them.
# Syntax highlight
Something seemingly as basic as syntax highlighting can be implemented based on syntactic or semantic analysis of the code.
There's even a more primitive way of implementing it, where an editor would just highlight the language's keyword and not much else. If memory serves, back at the time, most editors and IDEs would do the latter -- just highlight the keywords and be done with it.
Nowadays, semantic highlight is usually the default. Each identifier gets colored with the color of the symbol it resolves to. A constant name is rendered as constant throughout the file, not just in its declaration. Same applies for parameter, property, class, function names and so on.
# Semantic "grep"
As in, find all usages of, for example, a function across the project:
- If the function is passed as an argument somewhere (to be invoked as a callback later) this is going to be included in the search results.
- If another class has a function with the same name, this isn't going to end up in the search results.
- If the function is overloaded, it's possible to search for usages of a specific overload or all of them.
- And so on.
I don't remember this being a thing in old IDEs either at all or it didn't work for anything but the most trivial code. Although software like GNU Global did aim to implement this functionality, my memory is it was very limited in practice.
# Semantic code completion
While IDEs from mid 90s did have code completion, a modern IDE improves on that too. E.g., if you invoke auto-completion while writing a bit of code that passes an argument to a function, a modern IDE is able to form a list of suggestions containing only symbols that are currently in scope and match the function's parameter type.
# Refactoring
I think this goes without explanation, would you agree? Modern IDEs offer more refactorings, theses refactorings are way more complex and work much more reliably.
# Dependencies source code browsing/decompilation
Editing to add in this one. How could I forget? Basically, if I want to go to the definition of a symbol that comes from a library, the IDE will either download the source code if available or decompile the binary and and take me to the definition. To me personally, this is huge.