Hopp til hovedinnhold

Teknologi / 5 minutter /

Tips i IDEA: Skriv om koden din automatisk!

Her om dagen oppdaterte jeg java-versjonen i prosjektet jeg jobber med, og oppdaget at konstruktøren til URL var deprecated:

1/**
2 * Creates a {@code URL} object from the {@code String}
3 * representation.
4 * ...
5 * @deprecated Use {@link URI#toURL()} to construct an instance of URL. See the note on
6 * <a href="#constructor-deprecation">constructor deprecation</a> for more
7 * details.
8 */
9@Deprecated(since = "20")
10public URL(String spec) throws MalformedURLException {
11    this(null, spec);
12}

Et kjapt søk viste at jeg hadde brukt denne konstruktøren i underkant av 100 ganger, og nå var byggeloggen min full av advarsler om at denne måtte byttes ut. Jeg hadde ikke lyst til å skrive om koden for hånd. Det er tross alt en av de beste grunnene til at vi har datamaskiner, nemlig at de kan gjøre kjedelige oppgaver for oss, både raskere og bedre.

Men hvordan kan jeg skrive om all denne koden automatisk?

Første forsøk: search and replace, eller kanskje regex?

Det første jeg tenkte på var search and replace. Søk på bruk av URL-konstruktøren, og bytt det ut med URI(...).toURL(). Problemet her er at vi bruker URI i stedet for URL, så importene må oppdateres samtidig. Det får vi ikke gjort med search and replace, så vi er like langt, og må gå gjennom koden for hånd etterpå.

En naturlig forlengelse av denne ideen er regex, men siden jeg ikke ville ha enda flere problemer enn jeg allerede hadde, lot jeg den strategien ligge.

Structural search and replace

Fra noen smarte kollegaer i Kantega fikk jeg et tips om at det finnes en bedre search and replace i IDEA, som heter Structural search and replace. Her er det Jetbrains selv sier om det:

A conventional search process does not take into account the syntax and semantics of the source code. Even if you use regular expressions, IntelliJ IDEA still treats your code as a regular text. The structural search and replace (SSR) actions let you search for a particular code pattern or grammatical construct in your code considering your code structure.

Dette hørtes lovende ut! Search and replace som ikke bare ser på koden som tekst, men som i tillegg vet om strukturen og gramatikken i språket du jobber med.

Et eksempel

1import java.net.URL
2
3fun main() {
4    println("Hello World!")
5
6    val urls = SevenURLs(
7        url1 = URL("https://www.url1.com"),
8        url2 = URL("https://www.url2.com"),
9        url3 = URL("https://www.url3.com"),
10        url4 = URL("https://www.url4.com"),
11        url5 = URL("https://www.url5.com"),
12        url6 = URL("https://www.url6.com"),
13        url7 = URL("https://www.url7.com")
14    )
15
16    println("URLs: $urls")
17}
18
19data class SevenURLs(
20    val url1: URL,
21    val url2: URL,
22    val url3: URL,
23    val url4: URL,
24    val url5: URL,
25    val url6: URL,
26    val url7: URL
27)

I dette lille eksempelet har vi syv URL-er samlet i en dataklasse, som blir printet ut. Et tøysete eksempel, men nok for å vise hvordan dette fungerer. I koden jeg jobbet med hadde jeg i underkant av hundre steder som måtte oppdateres, og rundt femten forskjellige filer.

Målet er å oppdatere alle referansene til URL(...), og bytte de ut med URI(...).toURL(). I tillegg må vi fikse importene, sånn at java.net.URI blir importert, hvis det trengs, og java.net.URL blir fjernet, hvis den ikke brukes lenger.

Det første vi gjør er å velge “Replace structurally…” i Edit-menyen.

Edit-meny
Edit-meny

Da får vi opp et nytt vindu hvor vi kan søke og erstatte kode, som vi er kjent med fra vanlig search and replace. Det som er nytt her er at vi har code completion i søkevinduet, og kan velge blant mange ferdig definerte kodestrukturer som vi vil søke etter. Vi velger “Method calls” i kotlin.

Vindu for å søke og erstatte kode
Vindu for å søke og erstatte kode

Vi får presentert følgende mal: $Before$.$MethodCall$($Parameter$). $Before$ trenger vi ikke, så den fjerner vi. Når vi klikker på $MethodCall$ kan vi velge å legge til modifiers, for å filtrere ut de metodekallene vi er ute etter. Vi legger til en ny modifier ved å klikke på “+” helt til høyre, velger “Text” og skriver inn “URL”.

$Parameter$ er naturlig nok parameterene til metodekallet. I vårt tilfelle er vi interessert i konstruktørkallet med ett parameter, så vi endrer “Count” til å ha minimum 1 og maksimum 1 parameter.

I tekstfeltet under kan vi skrive inn en Replace template, som er en mal for hvordan den nye koden skal se ut. Vi vil endre metodekallet fra URL til URI, og vi vil kalle metoden toURL() på den nye instansen. Her kan vi bruke $Parameter$ for å få den samme parameteren som i søkeresultatet. Malen vår blir derfor java.net.URI($Parameter$).toURL().

I tillegg huker vi av “Shorten fully qualified names” under tekstfeltet. Da blir java.net.URI skrevet om til URI, og den riktige importen blir lagt til.

Når vi nå klikker på “Find” får vi opp et nytt vindu som finner alle kallene til URL-konstruktøren, og så er det bare å velge “Replace all” for å erstatte dem med URI-konstruktøren i stedet. Magisk ✨

1import java.net.URI
2import java.net.URL
3
4fun main() {
5    println("Hello World!")
6
7    val urls = SevenURLs(
8        url1 = URI("https://www.url1.com").toURL(),
9        url2 = URI("https://www.url2.com").toURL(),
10        url3 = URI("https://www.url3.com").toURL(),
11        url4 = URI("https://www.url4.com").toURL(),
12        url5 = URI("https://www.url5.com").toURL(),
13        url6 = URI("https://www.url6.com").toURL(),
14        url7 = URI("https://www.url7.com").toURL()
15    )
16
17    println("URLs: $urls")
18}
19
20// Dataklassen er ikke vist, siden den ikke er forandret.

Et fantastisk verktøy

Dette var en nydelig oppdagelse. Jeg trenger ikke å ha dårlig samvittighet for alle advarslene i koden som jeg burde ha gjort noe med, og jeg trenger ikke å skrive om masse kode for hånd, noe som hadde vært utrolig kjedelig. Datamaskiner skal helst ta seg av det som er kjedelig, så vi kan bruke tida vår på mer interessante oppgaver. Her har IDEA løst det på en suveren måte, selv om det var litt vanskelig å finne.

Det er mange flere muligheter i dette søket, som helt sikkert kan løse mange andre problemer. Husk på det til neste gang du kvier deg for å oppdatere masse kode i prosjektet ditt. Kanskje IDEA kan gjøre det for deg?