2012-03-15 9 views
8

Próbuję wygenerować kod JavaScript, używając Text.PrettyPrint. Problem polega na tym, że nest wytwarza ogromne wcięcia, gdy umieszcza się je obok innego elementu z ładnym drukiem. Na przykład, w tym kodzie:Text.PrettyPrint: Początkowe wcięcie od lewego marginesu

import Text.PrettyPrint 

fun :: Doc 
fun = vcat [ text "function" <+> lbrace 
      , nest 4 $ vcat $ replicate 5 $ text "// foo" 
      , rbrace 
      ] 

var :: Doc 
var = text "var" <+> text "x" 

test :: Doc 
test = var <+> equals <+> fun <> semi 

fun zaczyna się na kolumnę 9 test (ze względu var <+> equals <> empty do lewej strony), a tym samym jego kolejne linie są przesunięte o 9 + 4 = 13 kolumny:

var x = function { 
      // foo 
      // foo 
      // foo 
      // foo 
      // foo 
     }; 

Czy istnieje sposób, aby uczynić wcięcia od lewego marginesu, tak że powyższe byłyby pozbawione zamiast jak

var x = function { 
    // foo 
    // foo 
    // foo 
    // foo 
    // foo 
}; 

?

+2

Ładna drukarka Daan Leijen 'wl-pprint' ma bardziej elastyczną obsługę wcięć niż ładna drukarka Hughes Peyton-Jones. Zamiast tego możesz rozważyć użycie go. Zobacz dokumentację do dokumentacji, jest znacznie bardziej szczegółowy niż dokumentacja Haddock. –

+1

Myślę, że 'wl-pprint' okaże się właściwym rozwiązaniem - jestem gotów zaakceptować to jako odpowiedź, jeśli umieścisz to jako takie. – Cactus

+0

@Cactus czy kiedykolwiek pracowałeś z 'wl-pprint'? Jeśli tak, dlaczego nie dodasz na nie odpowiedzi? – Alec

Odpowiedz

2

Roztwór istocie używać wl-pprint (oraz zastąpienia nest z indent). Następnie podany kod jest odpowiednio zgodny z wymaganiami. Dla każdego coś jeszcze intencyjny o współpracy z starając się włamać na pretty pamiętać, że chociaż dla Doc konstruktorzy nie są narażone, nadal można się do nich dostać poprzez Generic z -XPatternSynonyms:

-- | Means of exposing the data constructors of `Doc` from `pretty` 
pattern GEmpty    = M1 (L1 (L1 (L1 (M1 U1)))) 
pattern GNilAbove doc  = M1 (L1 (L1 (R1 (M1 (M1 (K1 doc)))))) 
pattern GTextBeside d doc = M1 (L1 (R1 (L1 (M1 (M1 (K1 d) :*: M1 (K1 doc)))))) 
pattern GNest n doc   = M1 (L1 (R1 (R1 (M1 (M1 (K1 n) :*: M1 (K1 doc)))))) 
pattern GUnion ldoc rdoc = M1 (R1 (L1 (L1 (M1 (M1 (K1 ldoc) :*: M1 (K1 rdoc)))))) 
pattern GNoDoc    = M1 (R1 (L1 (R1 (M1 U1)))) 
pattern GBeside ldoc s rdoc = M1 (R1 (R1 (L1 (M1 (M1 (K1 ldoc) :*: M1 (K1 s) :*: M1 (K1 rdoc)))))) 
pattern GAbove ldoc b rdoc = M1 (R1 (R1 (R1 (M1 (M1 (K1 ldoc) :*: M1 (K1 b) :*: M1 (K1 rdoc)))))) 

Problemem jest najczęściej nie naruszono którykolwiek z wielu niezmienników, które biblioteka ma pod maską.


Na marginesie, ja również wl-pprint-annotated, nowoczesną ponowny zapis wl-pprint, z których jeden ma dostęp do podstawowych konstruktorów danych (kosztem konieczności pamiętać o niezmienniki związane). To jest faktycznie pakiet, który wykorzystam.

W szczególności pozwala mi robić tego rodzaju bloku mocującego tak, że jeśli jest na tyle mała, będzie go tylko na jednej linii:

-- | Asserts a 'Doc a' cannot render on multiple lines. 
oneLine :: Doc a -> Bool 
oneLine (WL.FlatAlt d _) = oneLine d 
oneLine (WL.Cat a b) = oneLine a && oneLine b 
oneLine (WL.Union a b) = oneLine a && oneLine b 
oneLine (WL.Annotate _ d) = oneLine d 
oneLine WL.Line = False 
oneLine _ = True 

-- | Make a curly-brace delimited block. When possible, permit fitting everything on one line 
block :: Doc a -> Doc a 
block b | oneLine b = hsep ["{", b, "}"] `WL.Union` vsep [ "{", indent 2 b, "}" ] 
     | otherwise = vsep [ "{", indent 2 b, "}" ] 

następnie uzyskać ładne wyniki automatycznie dzieje lub nie dzieje rozpiętość wiele linii:

ghci> "function" <> parens "x" <+> block ("return" <+> "x" <> semi) 
function(x) { return x; } 
ghci> "function" <> parens "x" <+> block ("x" <> "++" <> semi <#> "return" <+> "x" <> semi) 
function(x) { 
    x++; 
    return x; 
} 
2
 
offset = 1 + length (render $ var <+> equals) 
hang empty (negate offset) test 
+0

Nie wydaje się działać; w rzeczywistości wygląda tak bez tego dodatkowego "zawieszenia". Plus, czy nie miałoby to strasznego wpływu na wydajność? – Cactus

+0

@Cactus - grr, markdown zjadł mojego operatora "<+>". Naprawiono –

+0

Nie, to nie jest brak '<+>', wymyśliłem to samo. Ale otrzymuję ten sam wynik. – Cactus

1

Można osiągnąć pożądany rezultat poprzez zastosowanie vcat do listy gdzie pierwsza pozycja obejmuje również definicji zmiennej i przypisanie.

przykład:

fun :: Doc 
fun = vcat [ var <+> equals <+> text "function" <+> lbrace 
      , nest 4 $ vcat $ replicate 5 $ text "// foo" 
      , rbrace 
      ] 

var :: Doc 
var = text "var" <+> text "x" 

test :: Doc 
test = fun <> semi 
+1

Oczywiście, że mogę to zrobić - problemem jest to, że chcę, aby cała "funkcja {...}" nadal była jednym 'Doc' (a nie listą linii). W ten sposób 'ładny' nadal może wybrać dla mnie ładny układ (tzn. Czy' funkcja {..} 'musi nawet rozciągać się na wiele linii). – Alec

+0

Dobrze, ale nie mogłem znaleźć innego (bardziej eleganckiego) sposobu na zrobienie tego. Głównym problemem jest to, że wstawiasz 'var'' Doc' _beside_ the "fun'' Doc', to ustawi przesunięcie wcięcia "fun" zgodnie z długością doc, do którego został dołączony. Wręcz przeciwnie, łączenie dwóch dokumentów, jeden do drugiego, zachowa nietrafione przesunięcie w dokumencie, chyba że jeden z dwóch dokumentów jest "gniazdem". –