<?xml version="1.0" encoding="UTF-8"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
    <title>HanLHo. - Fractional Architect &amp; Software Product Engineer - scala-cli</title>
    <link rel="self" type="application/atom+xml" href="https://hanlho.com/tags/scala-cli/atom.xml"/>
    <link rel="alternate" type="text/html" href="https://hanlho.com"/>
    <generator uri="https://www.getzola.org/">Zola</generator>
    <updated>2024-05-14T00:00:00+00:00</updated>
    <id>https://hanlho.com/tags/scala-cli/atom.xml</id>
    <entry xml:lang="en">
        <title>How to extract all TODOs from code using Scala-CLI</title>
        <published>2024-05-14T00:00:00+00:00</published>
        <updated>2024-05-14T00:00:00+00:00</updated>
        
        <author>
          <name>
            
              Unknown
            
          </name>
        </author>
        
        <link rel="alternate" type="text/html" href="https://hanlho.com/p/how-to-extract-all-todos-from-code-using-scala-cli/"/>
        <id>https://hanlho.com/p/how-to-extract-all-todos-from-code-using-scala-cli/</id>
        
        <content type="html" xml:base="https://hanlho.com/p/how-to-extract-all-todos-from-code-using-scala-cli/">&lt;p&gt;In recent years, &lt;a href=&quot;https:&#x2F;&#x2F;scala-cli.virtuslab.org&quot;&gt;Scala CLI&lt;&#x2F;a&gt; has replaced sbt for my home and smaller work projects. Scala CLI lacks the extensive plugin ecosystem of sbt, so you need to write any additional functionalities yourself. Fortunately, writing simple scripts is a primary use case for Scala CLI, which we&#x27;ll leverage here to create the needed functionality.&lt;&#x2F;p&gt;
&lt;p&gt;What I needed was a straightforward method to list all &#x27;TO DO&#x27;s and related comments in the code.&lt;&#x2F;p&gt;
&lt;p&gt;Specifically the functionality is to list comments looking like:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#eff1f5;color:#4f5b66;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span style=&quot;color:#a7adba;&quot;&gt;&#x2F;&#x2F; TODO: A to do I want to keep on my rader.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a7adba;&quot;&gt;&#x2F;&#x2F; FUN: Some small tyding I could to improve the code in lost time.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a7adba;&quot;&gt;&#x2F;&#x2F; FIXME: A probably minor thing to fix.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a7adba;&quot;&gt;&#x2F;&#x2F; XXX: Anything else I do not want to forget about.
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;Here&#x27;s the full code. I&#x27;ll focus on the key aspects of the functionality and how to run the script.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#eff1f5;color:#4f5b66;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span style=&quot;color:#a7adba;&quot;&gt;&#x2F;** Find all TODOs and related tags in the source code of a project.
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a7adba;&quot;&gt; * To run using Scala CLI: `scala-cli run &amp;lt;this script&amp;gt;`
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a7adba;&quot;&gt; **&#x2F;
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a7adba;&quot;&gt;&#x2F;&#x2F;&amp;gt; using scala 3.4.1
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a7adba;&quot;&gt;&#x2F;&#x2F;&amp;gt; using dep com.lihaoyi::os-lib:0.10.0
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; os.&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; scala.io.Source
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;import&lt;&#x2F;span&gt;&lt;span&gt; scala.util.matching.Regex
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a7adba;&quot;&gt;&#x2F;&#x2F; Change relative path to the directory containing the source code
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;val &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;todos &lt;&#x2F;span&gt;&lt;span&gt;= extractTodosFromDirectory(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;src&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;span&gt;todos.foreach(println)
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;extractTodosFromFile&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;filePath&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;Path&lt;&#x2F;span&gt;&lt;span&gt;): &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;List&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;] = 
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;val &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;source &lt;&#x2F;span&gt;&lt;span&gt;= Source.fromFile(filePath.toString)
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;val &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fileContent &lt;&#x2F;span&gt;&lt;span&gt;= &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;try&lt;&#x2F;span&gt;&lt;span&gt; source.mkString &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;finally&lt;&#x2F;span&gt;&lt;span&gt; source.close()
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;val &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;fileName &lt;&#x2F;span&gt;&lt;span&gt;= filePath.last
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;val &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;todoPattern&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;Regex &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;&#x2F;(\s)*(TODO|FUN|XXX|FIXME): (.*)&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&amp;quot;&amp;quot;.r
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;val &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;todos &lt;&#x2F;span&gt;&lt;span&gt;= todoPattern.findAllIn(fileContent).matchData.map{&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;m &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt;
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;val &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;tag &lt;&#x2F;span&gt;&lt;span&gt;= m.group(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;2&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;val &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;description &lt;&#x2F;span&gt;&lt;span&gt;= m.group(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;3&lt;&#x2F;span&gt;&lt;span&gt;)
&lt;&#x2F;span&gt;&lt;span&gt;    &lt;&#x2F;span&gt;&lt;span style=&quot;color:#96b5b4;&quot;&gt;s&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$tag&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;] &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$description&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt; (&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;$fileName&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;) &lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;
&lt;&#x2F;span&gt;&lt;span&gt;  }.toList
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span&gt;  todos
&lt;&#x2F;span&gt;&lt;span&gt;end extractTodosFromFile
&lt;&#x2F;span&gt;&lt;span&gt;
&lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;def &lt;&#x2F;span&gt;&lt;span style=&quot;color:#8fa1b3;&quot;&gt;extractTodosFromDirectory&lt;&#x2F;span&gt;&lt;span&gt;(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;directoryPath&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;): &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;List&lt;&#x2F;span&gt;&lt;span&gt;[&lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;String&lt;&#x2F;span&gt;&lt;span&gt;] = 
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;val &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;dir &lt;&#x2F;span&gt;&lt;span&gt;= os.pwd &#x2F;directoryPath
&lt;&#x2F;span&gt;&lt;span&gt;  &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;val &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;files &lt;&#x2F;span&gt;&lt;span&gt;= os.list(dir).filter(os.isFile(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;_&lt;&#x2F;span&gt;&lt;span&gt;))
&lt;&#x2F;span&gt;&lt;span&gt;  files.flatMap(&lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;file &lt;&#x2F;span&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;=&amp;gt;&lt;&#x2F;span&gt;&lt;span&gt; extractTodosFromFile(file)).toList
&lt;&#x2F;span&gt;&lt;span&gt;end extractTodosFromDirectory
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;noteworthy-about-the-script&quot;&gt;Noteworthy about the script&lt;&#x2F;h3&gt;
&lt;p&gt;To run the script, assuming you&#x27;ve named it &lt;code&gt;todo-list.sc&lt;&#x2F;code&gt;:&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;bash&quot; style=&quot;background-color:#eff1f5;color:#4f5b66;&quot; class=&quot;language-bash &quot;&gt;&lt;code class=&quot;language-bash&quot; data-lang=&quot;bash&quot;&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;scala-cli&lt;&#x2F;span&gt;&lt;span&gt; run todo-list.sc
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The script scans the &lt;code&gt;src&lt;&#x2F;code&gt; directory, located one level below the script. Change this to your desired location or modify the script to take this location as an argument.&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#eff1f5;color:#4f5b66;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;val &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;todos &lt;&#x2F;span&gt;&lt;span&gt;= extractTodosFromDirectory(&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;src&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;)
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;p&gt;The script searches for the following tags: &lt;code&gt;TODO|FUN|XXX|FIXME&lt;&#x2F;code&gt;&lt;&#x2F;p&gt;
&lt;pre data-lang=&quot;scala&quot; style=&quot;background-color:#eff1f5;color:#4f5b66;&quot; class=&quot;language-scala &quot;&gt;&lt;code class=&quot;language-scala&quot; data-lang=&quot;scala&quot;&gt;&lt;span style=&quot;color:#b48ead;&quot;&gt;val &lt;&#x2F;span&gt;&lt;span style=&quot;color:#bf616a;&quot;&gt;todoPattern&lt;&#x2F;span&gt;&lt;span&gt;: &lt;&#x2F;span&gt;&lt;span style=&quot;color:#d08770;&quot;&gt;Regex &lt;&#x2F;span&gt;&lt;span&gt;= &amp;quot;&amp;quot;&amp;quot;&lt;&#x2F;span&gt;&lt;span style=&quot;color:#a3be8c;&quot;&gt;&#x2F;&#x2F;(\s)*(TODO|FUN|XXX|FIXME): (.*)&lt;&#x2F;span&gt;&lt;span&gt;&amp;quot;&amp;quot;&amp;quot;.r
&lt;&#x2F;span&gt;&lt;&#x2F;code&gt;&lt;&#x2F;pre&gt;
&lt;h3 id=&quot;sidebar-custom-code-or-plugins&quot;&gt;Sidebar: custom code or plugins?&lt;&#x2F;h3&gt;
&lt;p&gt;For simple tasks, writing and maintaining your own code is often easier than managing a plugin:&lt;&#x2F;p&gt;
&lt;ul&gt;
&lt;li&gt;
&lt;p&gt;A plugin requires some understanding and configuration.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;A plugin may not meet your needs exactly, for example, also scanning for a &#x27;FUN&#x27; tag.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;A plugin needs updating.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;A plugin can become unmaintained.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;li&gt;
&lt;p&gt;A plugin introduces third-party code, which may not always be secure.&lt;&#x2F;p&gt;
&lt;&#x2F;li&gt;
&lt;&#x2F;ul&gt;
&lt;p&gt;Writing this custom code took less than 30 minutes, including a diversion into exploring scala-native. Once functional, it seldom needs updates. The main downside is its dependency on the &lt;code&gt;os-lib&lt;&#x2F;code&gt;library, but that&#x27;s a compromise I accept to avoid spending more time on this script versus managing the library. If necessary, removing this dependency is straightforward.&lt;&#x2F;p&gt;
&lt;p&gt;Thank you for reading!&lt;&#x2F;p&gt;
&lt;p&gt;Hans&lt;&#x2F;p&gt;
</content>
        
    </entry>
</feed>
