LINUX.ORG.RU

Отсортировать импорты в файле - unix way

 , ,


0

2

Всем привет.

Имеем java/scala-like секцию импорта в начале файла, вроде:

package blah

import argonaut._
import scala.concurrent.Future
import scalaz._

import types._
import impl.DBOperations

// code follows
case class Foo()
// ...

Нужно написать простую command-line тулзу, которая принимает на вход файл и сортирует в нём импорты. Пустые строки между импортами нужно удалить. Знаю, что это можно сделать как-то очень просто unix-way-но, но к сожалению в этой области у меня пробел. Поможете?

★★★★

Строго настрого не использовать в продакшне!

#!/bin/bash

FROM=`grep -n '^import' $1 | cut -d\: -f1 | head -1`
TO=`grep -n '^import' $1 | cut -d\: -f1 | tail -1`

TMP_FILE=`mktemp`

trap_handler(){
	rm $TMP_FILE
}

trap trap_handler EXIT

F=$(($FROM-1))
S=$(($TO - $FROM + 1))
T=$(($TO+1))

cat $1 | head -$F | egrep -v "^[[:space:]]*$" > $TMP_FILE
echo " " >> $TMP_FILE
cat $1 | tail -n+$FROM | head -$S | sort | uniq | egrep -v "^[[:space:]]*$" >> $TMP_FILE
echo " " >> $TMP_FILE
cat $1 | tail -n+$T | egrep -v "^[[:space:]]*$" >> $TMP_FILE

cat $TMP_FILE>$1

Для каталога

$ find ./ -iname '*.scala' | xargs -I {} ./sort_imports.sh {} 
vertexua ★★★★★
()
Последнее исправление: vertexua (всего исправлений: 2)
Ответ на: комментарий от vertexua

А мне обычно лень разбираться в юникс-вее и я в таких случаях пишу какую-нибудь фигню типа

{-# LANGUAGE OverloadedStrings #-}

import Data.Char
import Data.List
import Control.Arrow
import Control.Applicative
import System.Environment
import qualified Data.Text as T
import qualified Data.Text.IO as T

main :: IO ()
main = do
  ls <- T.lines <$> (T.readFile =<< head <$> getArgs)
  let (hs, is'etc) = break isImport ls
      (is'ss, ts) = span (\line -> isImport line || isEmpty line) is'etc
      (ss, is) = second reverse $ span isEmpty $ reverse is'ss
      sis = filter (not . isEmpty) $ sort is
  T.putStrLn $ T.unlines $ hs ++ sis ++ ss ++ ts
  where
    isEmpty = T.all isSpace
    isImport = ("import " `T.isInfixOf`)
quasimoto ★★★★
()

это можно сделать как-то очень просто unix-way-но

Это интересно, особенно с учетом того как import используется в scala. Хотелось бы увидеть рабочее решение на unix-way «костылях» хотя бы для такого случая:

import scala.collection._
import mutable._

object TestImports extends App {
  println(ListBuffer(1, 2, 3))
}
kamre ★★★
()
Ответ на: комментарий от kamre

Мне нужно просто отсортировать, безо всякой магии. Навеяло этим. Но идея не умеет.

Большое спасибо отписавшимся выше, завтра уже буду пробовать.

Bohtvaroh ★★★★
() автор топика

поздняя ночь, поэтому кодить на awk уже лень..

смысл действа - выдать порцию файла до первой встречи «import» в первом поле, далее все строки с «import» кидать в массив и как только встретиться нечто отличное от 'import' или комментария - выдать отсортированный массив и всю оставшуюся часть файла. Может даже получится длииииинный однострочник :)

на sed это совсем муторно, с grep/sort/head/tail - долго. правильное средство AWK.

MKuznetsov ★★★★★
()

кстати если у scala нормальная интроспекция, то можно в начало файла добавить чуток (чтоб подавить исполнение) и в конец чуток (чтоб поймать результат) - и получить список импорта..

честно - не знаю, способна ли на такое scala :-)

MKuznetsov ★★★★★
()
Ответ на: комментарий от MKuznetsov

Ничего не понял. При чём тут интроспекция?

Bohtvaroh ★★★★
() автор топика

Скалу хорошо поддерживает юниксвейная утилита rm (Rearrange Module names).

anonymous
()

ssam '/^import/,$-/^import/ | sort', правда plan9port под рукой нету, проверить не могу

anonymous
()

vim (echo -e - непортабелен): echo -e '/import/,$-?import? !sort | grep . \n %p' | ex <file>

anonymous
()

интересно как вы код писали, почему импорты изначально неупорядочены?

Да и вообще, зачем это надо делать, если уж изначально криво всё?

ii8_ ★★★★
()

К сожалению пока ни один вариант не завёлся.

Первый:

./sort.sh Versioning.scala 
usage: mktemp [-d] [-q] [-t prefix] [-u] template ...
       mktemp [-d] [-q] [-u] -t prefix 
./sort.sh: line 18: $TMP_FILE: ambiguous redirect
./sort.sh: line 19: 1: ambiguous redirect
./sort.sh: line 20: $TMP_FILE: ambiguous redirect
./sort.sh: line 21: 1: ambiguous redirect
./sort.sh: line 22: $TMP_FILE: ambiguous redirect
^Cusage: rm [-f | -i] [-dPRrvW] file ...
       unlink file

Может, потому что у меня OSX.

Второй очень близок, но вставляет импорты в конец.

ssam не знаю, что такое. Vim - не хочу.

Bohtvaroh ★★★★
() автор топика
Ответ на: комментарий от Bohtvaroh

Второй очень близок, но вставляет импорты в конец.

Например? Оно может делать

import anorm.SqlParser._
import anorm._

вместо

import anorm._
import anorm.SqlParser._

потому что 'S' < '_', так что нужна кастомная сортировка:

{-# LANGUAGE OverloadedStrings #-}

import Data.Char
import Data.List
import Data.Function
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Control.Arrow
import Control.Applicative
import System.Environment

main :: IO ()
main = do
  ls <- T.lines <$> (T.readFile =<< head <$> getArgs)
  let (hs, is'etc) = break isImport ls
      (is'ss, ts) = span (\line -> isImport line || isEmpty line) is'etc
      (ss, is) = second reverse $ span isEmpty $ reverse is'ss
      sis = dropWhile isEmpty $ sortBy importSorter is
      -- ^ was: filter (not . isEmpty) $ sort is
  mapM_ T.putStrLn hs; mapM_ T.putStrLn sis; mapM_ T.putStrLn ss; mapM_ T.putStrLn ts
  -- ^ was: T.putStrLn $ T.unlines $ hs ++ sis ++ ss ++ ts
  where
    isEmpty = T.all isSpace
    isImport = ("import " `T.isInfixOf`)
    importSorter = compare `on` trick_ where trick_ = T.replace "_" "!"
    -- ^ since '!' == chr 33 is small enough

Ещё желательно писать в другой файл. В остальном должно работать — сортировать строки с «import » в согласии с importSorter в шапке файла (import вложенные в прочие места дальше останутся как есть, естественно).

quasimoto ★★★★
()
Ответ на: комментарий от quasimoto

На входе:

package com.blah.cv.repository.api

import argonaut._
import scala.concurrent.Future
import scalaz._, Scalaz._
import types._
import impl.DBOperations
import common.{FutureUtils, OptionUtils}
import model.api.{HasId, HasVersion}
import reactivemongo.bson.{BSONDouble, BSONDocument}

trait Test {
  //

  def f(): Unit

  //

  //
}

На выходе:






  //
  //
  //
  def f(): Unit
import argonaut._
import common.{FutureUtils, OptionUtils}
import impl.DBOperations
import model.api.{HasId, HasVersion}
import reactivemongo.bson.{BSONDouble, BSONDocument}
import scala.concurrent.Future
import scalaz._, Scalaz._
import types._
package com.blah.cv.repository.api
trait Test {
}
Bohtvaroh ★★★★
() автор топика
Ответ на: комментарий от Bohtvaroh

Чудеса какие-то, УМВР:

➜  ~  cat t
package com.blah.cv.repository.api

import argonaut._
import scala.concurrent.Future
import scalaz._, Scalaz._
import types._
import impl.DBOperations
import common.{FutureUtils, OptionUtils}
import model.api.{HasId, HasVersion}
import reactivemongo.bson.{BSONDouble, BSONDocument}

trait Test {
  //

  def f(): Unit

  //

  //
}
➜  ~  cat SortImports.hs
{-# LANGUAGE OverloadedStrings #-}

import Data.Char
import Data.List
import Data.Function
import qualified Data.Text as T
import qualified Data.Text.IO as T
import Control.Arrow
import Control.Applicative
import System.Environment

main :: IO ()
main = do
  ls <- T.lines <$> (T.readFile =<< head <$> getArgs)
  let (hs, is'etc) = break isImport ls
      (is'ss, ts) = span (\line -> isImport line || isEmpty line) is'etc
      (ss, is) = second reverse $ span isEmpty $ reverse is'ss
      sis = dropWhile isEmpty $ sortBy importSorter is
  mapM_ T.putStrLn hs; mapM_ T.putStrLn sis; mapM_ T.putStrLn ss; mapM_ T.putStrLn ts
  where
    isEmpty = T.all isSpace
    isImport = ("import " `T.isInfixOf`)
    importSorter = compare `on` trick_ where trick_ = T.replace "_" "!"
➜  ~  ghc -O3 SortImports.hs
[1 of 1] Compiling Main             ( SortImports.hs, SortImports.o )
Linking SortImports ...
➜  ~  ./SortImports t          
package com.blah.cv.repository.api

import argonaut._
import common.{FutureUtils, OptionUtils}
import impl.DBOperations
import model.api.{HasId, HasVersion}
import reactivemongo.bson.{BSONDouble, BSONDocument}
import scala.concurrent.Future
import scalaz._, Scalaz._
import types._

trait Test {
  //

  def f(): Unit

  //

  //
}
quasimoto ★★★★
()
Ответ на: комментарий от Bohtvaroh

Тогда покажи выхлоп (прямо со stdout) с таким main

main = do
  txt <- T.readFile . head =<< getArgs
  print txt
  let ls = T.lines txt
  print ls
  let (hs, is'etc) = break isImport ls
  print hs; print is'etc
  let (is'ss, ts) = span (\line -> isImport line || isEmpty line) is'etc
  print is'ss; print ts
  let (ss, is) = second reverse $ span isEmpty $ reverse is'ss
  print ss; print is
  let sis = filter (not . isEmpty) $ sort is
  print sis
  let r1 = hs ++ sis ++ ss ++ ts
  print r1
  let r2 = T.unlines $ hs ++ sis ++ ss ++ ts
  print r2
  T.putStrLn r2
  where isEmpty = T.all isSpace; isImport = ("import " `T.isInfixOf`)

и таким

main = do
  txt <- readFile . head =<< getArgs
  print txt
  let ls = lines txt
  print ls
  let (hs, is'etc) = break isImport ls
  print hs; print is'etc
  let (is'ss, ts) = span (\line -> isImport line || isEmpty line) is'etc
  print is'ss; print ts
  let (ss, is) = second reverse $ span isEmpty $ reverse is'ss
  print ss; print is
  let sis = filter (not . isEmpty) $ sort is
  print sis
  let r1 = hs ++ sis ++ ss ++ ts
  print r1
  let r2 = unlines $ hs ++ sis ++ ss ++ ts
  print r2
  putStrLn r2
  where isEmpty = all isSpace; isImport = ("import " `isInfixOf`)
quasimoto ★★★★
()

tac | sed -e '/^import / { H ; d }' -e '$ x' | tac

нужно добавить правильную реакцию на пробелы, работу с файлами, сохранность комментариев, оно очевидно.

DonkeyHot ★★★★★
()
Ответ на: комментарий от quasimoto

Посыпаю голову пеплом, my bad, всё правильно работает. Спасибо ещё раз. Это даже лучше чем unix-way. :)

Bohtvaroh ★★★★
() автор топика

tsort, топологическая сортировка модулей из GNU coreutils

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.