{"id":702337,"date":"2026-04-21T15:25:37","date_gmt":"2026-04-21T14:25:37","guid":{"rendered":"https:\/\/blog.jetbrains.com\/?post_type=scala&#038;p=702337"},"modified":"2026-04-21T17:06:13","modified_gmt":"2026-04-21T16:06:13","slug":"incremental-highlighting","status":"publish","type":"scala","link":"https:\/\/blog.jetbrains.com\/ko\/scala\/2026\/04\/21\/incremental-highlighting","title":{"rendered":"Incremental Highlighting for Scala"},"content":{"rendered":"<p><em>If an error is detected in a file but no one sees it, is it really highlighted?<\/em><\/p>\n<p>This is a tale about why highlighting only what\u2019s visible is both the strangest and the most reasonable thing to do \u2013 and how you can benefit from it.<\/p>\n<p><em>(Enable Settings | Scala | Editor | Highlighting Mode | Incremental to speed up highlighting and reduce resource consumption.)<\/em><\/p>\n<h2 id=\"compiling-highlights\">Compiling highlights<\/h2>\n<p>Both IDEs and compilers analyze code, but they do so differently. Compilers, for example:<\/p>\n<ul>\n<li>Translate source code into executable code.<\/li>\n<li>Process every source file.<\/li>\n<li>Treat errors as obstacles to the main goal.<\/li>\n<\/ul>\n<p>Incremental \u201ccompilers\u201d can prevent recompilation, but they also eventually compile everything.<\/p>\n<p>IDEs, on the other hand:<\/p>\n<ul>\n<li>Analyze code for the purpose of understanding and editing.<\/li>\n<li><strong>Highlight<\/strong> errors in the context of the current task.<\/li>\n<li>Process only what\u2019s necessary and can tolerate errors.<\/li>\n<\/ul>\n<p>If one source file doesn\u2019t compile, the compiler won\u2019t process another file that depends on it. An IDE, however, can highlight code that depends on a file with errors, and there might be no errors in the highlighted code itself.<\/p>\n<p>So far, so good. However, even though IDE <strong>algorithms<\/strong> do not depend on files, the user <strong>interface<\/strong> does. Just as you run <code>scalac Foo.scala<\/code>, you open <code>Foo.scala<\/code> in an editor tab. The IDE processes the entire file, even if it\u2019s very large and most of it is not visible or relevant to the task at hand. Because of this, highlighting depends on where you place classes and methods. If you put <code>class Foo<\/code> and <code>class Bar<\/code> in separate files, <code>Foo<\/code> is not highlighted when you edit <code>Bar<\/code>; but if both classes are in the same file, viewing or editing one class also highlights the other.<\/p>\n<p>For compilers, a UI that processes files in their entirety is natural, because that\u2019s how they work. IDEs, however, do not have this limitation. In contrast to a compiler, IntelliJ IDEA can re-highlight only part of a file without recomputing everything. Nevertheless, there is still an initial highlighting, and not every modification can be localized. <strong>The IDE keeps the entire file highlighted, similar to how a compiler keeps the entire project compiled \u2013 but it doesn\u2019t have to. What if we could do better?<\/strong><\/p>\n<h2 id=\"the-file-is-a-lie\">The file is a lie<\/h2>\n<p>Consider the following:<br \/>\n<img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2026\/04\/incremental-highlighting-code.png\"><br \/>\n<span style=\"margin-top: 1em;\">&nbsp;<\/span><br \/>\nLooks like a code snippet, huh? But no, it\u2019s a \u201cfile\u201d:<br \/>\n<img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2026\/04\/incremental-highlighting-file.png\"><\/p>\n<p>We take this UI pattern for granted. However, if you think about it, such a \u201cfile\u201d is no different from a \u201cfile\u201d in the <em>Project<\/em> view: the title shows the name, just like nodes do; the scrollbar provides navigation, like the tree does; the error stripe can show marks, and the tree can show marks as well. We don\u2019t really see the entire file, only a code snippet.<\/p>\n<p>Thus, the question of whether to highlight the entire file makes no sense \u2013 we cannot highlight elements that are not visible, just as we cannot highlight elements in files that are not open. (And \u201copening a directory\u201d in the <em>Project<\/em> view doesn\u2019t change that.) Note that we don\u2019t say that compilers \u201chighlight\u201d files; only IDEs do. <strong>We can only highlight what\u2019s visible. Beyond that, analysis is involved, but not highlighting.<\/strong><\/p>\n<h2 id=\"what-you-see-is-what-you-get\">What you see is what you get<\/h2>\n<p>Now, how much should an IDE analyze? Even though it\u2019s not possible to highlight code beyond the visible area, it is possible to draw marks on the error stripe, underline nodes in the <em>Project<\/em> view, or display errors in the <em>Problems<\/em> tool window. IntelliJ IDEA does all this. Yet, it doesn\u2019t analyze code in files that are not open. Why? Isn\u2019t more analysis better? (In principle, IDEs could process every single file, just as compilers do.) First, there are diminishing returns. Second, it is not without cost.<\/p>\n<p>Source code is not a collection of random symbols. Code has <strong>high cohesion<\/strong> and <strong>low coupling<\/strong>. The greater the distance between two points in code, the less interconnected they are. That\u2019s why highlighting code in the immediate context is more important, while highlighting distant code is less important. Moreover, the latter can only distract from the task at hand. If you\u2019re editing a method, you don\u2019t want to be distracted by thousands of errors in other files while you\u2019re still in the process of editing.<\/p>\n<p>At the same time, analysis consumes CPU, RAM, and battery. It can make the IDE or even the entire OS less responsive. In this highlighting economy, you want to <strong>maximize profit<\/strong> by balancing revenue and costs. But what is the proper scope to hit this Goldilocks zone, and is \u201cthe file\u201d the answer?<\/p>\n<p>To begin with, \u201cfile\u201d is not a language <strong>entity<\/strong> (and <strong>compilation units<\/strong> don\u2019t have to be files). It\u2019s actually an implementation detail of how we store source code. In principle, we could store code as an abstract syntax tree (AST) in a database; then there would be no files. Metrics such as <strong>cyclomatic complexity<\/strong> are about packages, classes, and methods \u2013 not files. It could be that the distance in code is more important than the file boundary.<\/p>\n<p>Another issue with files is that they can be arbitrarily large. In principle, you could put all project classes into a single file. This would not affect the bytecode, but it would make source code more costly to highlight. (Although there is some benefit to detecting errors in the entire file, this doesn\u2019t guarantee the absence of errors in general \u2013 for that, you need to compile the project anyway.)<\/p>\n<p>Now consider the visible area: that\u2019s where we can actually highlight code. It is the focal point of attention and the location of the cursor, where feedback is immediate rather than indirect. The benefits of analyzing code in the visible area are the greatest. At the same time, the visible area is naturally bounded by display resolution, human vision, and human comprehension, and the cost of analysis does not depend on file size. <strong>It could be that \u201cvisible area\u201d is a better choice for the scope than \u201cfile.\u201d<\/strong> (We can also extend this logic to folding and skip parts that are folded and not actually visible.)<\/p>\n<h2 id=\"implementation\"><code>???<\/code><\/h2>\n<p>Fortunately, this is not just a theory \u2013 you can already try it in practice! <em>Incremental<\/em> highlighting can now be enabled in <em>Settings | Scala | Editor | Highlighting Mode<\/em>:<br \/>\n<img decoding=\"async\" src=\"https:\/\/blog.jetbrains.com\/wp-content\/uploads\/2026\/04\/incremental-highlighting-settings.png\"><\/p>\n<p>The setting is only for Scala and is available only when you\u2019re using the built-in highlighting, not the compiler. The setting is per-project, so if you want to use this mode in multiple projects, you need to enable it in each one. (The capability has actually been available since 2024.3, but we recommend using the newest version to benefit from more improvements.)<\/p>\n<p>When incremental highlighting is enabled, only code in the visible area is highlighted (excluding folded parts). The algorithm can handle multiple editors, including split editors and the diff viewer.<\/p>\n<p>The mode is called \u201cincremental\u201d rather than \u201cpartial\u201d because, even though only part of a file is highlighted, the parts that have already been highlighted remain highlighted. If you scroll through the code back and forth, computations are cached and not duplicated. Furthermore, the mode works well with incremental updates on modifications; editing code in a local scope preserves the already highlighted ranges, even outside the visible area.<\/p>\n<p>To make scrolling smoother, the algorithm pre-highlights 15 lines before and after the visible area. (It\u2019s possible to customize this range by adjusting <code>scala.incremental.highlighting.lookaround<\/code> in the <a href=\"https:\/\/youtrack.jetbrains.com\/articles\/SUPPORT-A-1030\/How-to-edit-IntelliJ-IDE-Registry\" target=\"_blank\" rel=\"noopener\">registry<\/a>.) However, if you navigate to a definition in a file directly, you may observe on-the-fly highlighting, similar to when you open a file for the first time or navigate to definitions in other files.<\/p>\n<p>The error stripe (marks on the scrollbar) is filtered to include only data within the visible range. <em>Next\/Previous Highlighted Error<\/em> navigates between known \u2013 usually visible \u2013 errors. Several inspections, such as <em>Unused Declaration<\/em> and <em>Unused Import<\/em>, are not active. However, imports are normally folded anyway, and <em>Optimize Imports<\/em> does work. Many of these restrictions are accidental, intended to keep the implementation simple, and can be improved in future updates. (Select <em>Nightly<\/em> in <em>Settings | Languages | Scala | Updates<\/em> to get updates faster.)<\/p>\n<p>When the incremental highlighting mode is enabled, you can double-press the <em>Esc<\/em> key to analyze the entire file on demand, using all inspections.<\/p>\n<h2 id=\"profit\">Profit<\/h2>\n<p>The benefits of using incremental highlighting include:<\/p>\n<ol type=\"1\">\n<li>Better responsiveness<\/li>\n<li>Optimized CPU usage<\/li>\n<li>Efficient memory usage<\/li>\n<li>Cooler system temperatures<\/li>\n<li>Quieter operation<\/li>\n<li>Longer battery life<\/li>\n<\/ol>\n<p>This applies to both initial highlighting and re-highlighting \u2013 both when viewing and editing code. <strong>In many cases, highlighting time can be reduced by up to 5<\/strong>\u2013<strong>10 times<\/strong>, though the exact amount depends on the file size and code complexity. The benefits of incremental highlighting are especially noticeable for Scala code, which can be rather complex and difficult to analyze.<\/p>\n<p>The benefits also depend on hardware; if you have a powerful desktop machine with water cooling, the effect might be less noticeable, but if you use an ultrabook, the difference is more significant.<\/p>\n<h2 id=\"feedback-welcome\">Feedback welcome<\/h2>\n<p>Both the idea and the implementation are works in progress. Many parts can be refined and improved further. (See <a href=\"https:\/\/youtrack.jetbrains.com\/issue\/SCL-23216\" target=\"_blank\" rel=\"noopener\">SCL-23216<\/a> for more technical details.)<\/p>\n<p>We\u2019d love for you to <strong>try the feature and share your feedback<\/strong>. Please report any issues through <a href=\"https:\/\/youtrack.jetbrains.com\/issues\/SCL\" target=\"_blank\" rel=\"noopener\">YouTrack<\/a>. If you have any questions, feel free to ask us on <a href=\"https:\/\/discord.com\/channels\/931170831139217469\" target=\"_blank\" rel=\"noopener\">Discord<\/a>.<\/p>\n<p>Happy developing!<\/p>\n<p>The Scala team at JetBrains<\/p>\n","protected":false},"author":11,"featured_media":702464,"comment_status":"closed","ping_status":"closed","template":"","categories":[808,8058,6945],"tags":[40,225],"cross-post-tag":[],"acf":[],"_links":{"self":[{"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/scala\/702337"}],"collection":[{"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/scala"}],"about":[{"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/types\/scala"}],"author":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/users\/11"}],"replies":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/comments?post=702337"}],"version-history":[{"count":10,"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/scala\/702337\/revisions"}],"predecessor-version":[{"id":702511,"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/scala\/702337\/revisions\/702511"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/media\/702464"}],"wp:attachment":[{"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/media?parent=702337"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/categories?post=702337"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/tags?post=702337"},{"taxonomy":"cross-post-tag","embeddable":true,"href":"https:\/\/blog.jetbrains.com\/ko\/wp-json\/wp\/v2\/cross-post-tag?post=702337"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}