Haskell-ide-engine: Formatting gives different result than using Brittany on the command line.

Created on 16 Oct 2018  Â·  9Comments  Â·  Source: haskell/haskell-ide-engine

Setup:

  • brittany version 0.11.0.0 (stack exec -- brittany --version inside project dir yields same version as global brittany executable)
  • Spacemacs develop branch, installed HIE as recommended in this repo
  • HIE pulled from master as of today
  • No project-specific brittany config.

Original:

pop :: (MonadState EventGen m) => m Event
pop = do
  -- queueIsNull <- uses egQueue null
  -- Pass queue through trace OP
  -- let q = trace ("Queue is empty before pop: " ++ show queueIsNull) queue
  eKey <- statePart egQueue $ fromJust . Heap.view
  -- ^ Pop an identifier from the heap and retrieve the corresponding event
  -- from the hashmap. Then delete the it from the hashmaps.
  let eId = ekId eKey
  event <- fromJust <$> uses egEvents (Map.!? eId)
  -- Pass event through trace OP
  -- let event' =
  --       fromJust $
  --       trace
  --         ("Event in map is nothing (before fromJust): " ++
  --          show (isNothing event'))
  --         event
  modifyPart egEvents $ Map.delete eId
  case event ^. evType of
    END ch _ -> modifyPart egEndIds (Map.delete (view evCell event, ch))
    _ -> return ()
  return event

mkEventGen :: (MonadReader Opt m) => Word64 -> m EventGen
mkEventGen seed = execStateT fn (_mkEventGen seed)
  where
    fn :: (MonadRandom (StateT EventGen m), MonadReader Opt m) => (StateT EventGen m) ()
    fn = mapM_ (generateNewEvent 0.0) gridIdxs

Via terminal, stdin/out:

pop :: (MonadState EventGen m) => m Event
pop = do
  -- queueIsNull <- uses egQueue null
  -- Pass queue through trace OP
  -- let q = trace ("Queue is empty before pop: " ++ show queueIsNull) queue
  eKey <- statePart egQueue $ fromJust . Heap.view
  -- ^ Pop an identifier from the heap and retrieve the corresponding event
  -- from the hashmap. Then delete the it from the hashmaps.
  let eId = ekId eKey
  event <- fromJust <$> uses egEvents (Map.!? eId)
  -- Pass event through trace OP
  -- let event' =
  --       fromJust $
  --       trace
  --         ("Event in map is nothing (before fromJust): " ++
  --          show (isNothing event'))
  --         event
  modifyPart egEvents $ Map.delete eId
  case event ^. evType of
    END ch _ -> modifyPart egEndIds (Map.delete (view evCell event, ch))
    _        -> return ()
  return event

mkEventGen :: (MonadReader Opt m) => Word64 -> m EventGen
mkEventGen seed = execStateT fn (_mkEventGen seed)
 where
  fn :: (MonadRandom (StateT EventGen m), MonadReader Opt m) => (StateT EventGen m) ()
  fn = mapM_ (generateNewEvent 0.0) gridIdxs

Formatting inside of Spacemacs obtained by calling function lsp-format-buffer:

pop :: (MonadState EventGen m) => m Event
pop = do
  -- queueIsNull <- uses egQueue null
  -- Pass queue through trace OP
  -- let q = trace ("Queue is empty before pop: " ++ show queueIsNull) queue
        eKey <- statePart egQueue $ fromJust . Heap.view
        -- ^ Pop an identifier from the heap and retrieve the corresponding event
        -- from the hashmap. Then delete the it from the hashmaps.
        let eId = ekId eKey
        event <- fromJust <$> uses egEvents (Map.!? eId)
        -- Pass event through trace OP
        -- let event' =
        --       fromJust $
        --       trace
        --         ("Event in map is nothing (before fromJust): " ++
        --          show (isNothing event'))
        --         event
        modifyPart egEvents $ Map.delete eId
        case event ^. evType of
                END ch _ -> modifyPart egEndIds (Map.delete (view evCell event, ch))
                _        -> return ()
        return event

mkEventGen :: (MonadReader Opt m) => Word64 -> m EventGen
mkEventGen seed = execStateT fn (_mkEventGen seed)
    where
        fn :: (MonadRandom (StateT EventGen m), MonadReader Opt m) => (StateT EventGen m) ()
        fn = mapM_ (generateNewEvent 0.0) gridIdxs

Notice:

  • Indentation in pop after let q
  • 4 spaces on where indentation in mkEventGen

I don't exactly know what lsp-format-buffer calls behind the scenes. While not shown, both outputs clearly come from brittany, as neither hindent nor stylish formats imports in columns by default.

bug

Most helpful comment

Had the same problem with Emacs 26.2 and HIE on commit 0f697c8919747effca54be8a9b494896aea7d947

Fixed by adding this to the config:

(add-hook 'haskell-mode-hook (lambda ()  (setq tab-width 2)))

haskell-mode's indentation sets the tab-width to 8 locally overwriting any previous customisations.

All 9 comments

Do you maybe have a global brittany config file? I cannot think of any other reason

Yes, there is a global one.

  • Everyone has one AFAIK; it is installed with Brittany.
  • I had this problem before I modified mine
  • I changed mine to set column length to 100 and to allow hanging type sigs
  • Both outputs (terminal and in emacs) seem to respect the config, as is evident by fn having a single-line type signature (I think).

I was able to recreate this problem earlier this week but now after reinstalling hie it gives the expected output, 2 spaces, hanging type sigs and column length. @tsoernes Does reinstalling it fix this for you?

@bubba I'm not sure what reinstalling implies. I deleted hie, hie-wrapper and brittany from ~/.local/bin, then pulled freshest HIE and did stack install in that directory along with stack install brittany in that same directory. I still have the same problem after restarting Emacs.

I also tried deleting the global Brittany file. Both cmd-line usage and HIE responded to that (hanging indent setting) but the output from LSP/HIE is still messed up.

Can you check what your tab-size emacs setting is? Just from browsing here it looks like that's used to determine the tab size in the LSP request. (I believe tab size is the only formatting option hie overrides in Brittany, since its an argument to the request)

@bubba I assume you mean tab-width, since tab-size does not seem to exist and the link references the former.

tab-width is a variable defined in ‘C source code’.
Its value is 8
Local in buffer SimRunner.hs; global value is 2

  Automatically becomes buffer-local when set.
  This variable is safe as a file local variable if its value
  satisfies the predicate ‘integerp’.

Documentation:
Distance between tab stops (for display of tab characters), in columns.
NOTE: This controls the display width of a TAB character, and not
the size of an indentation step.

That's weird, because first of all, the incorrectly formatted code seems to use 4 not 8 (or 2 as it should) for indentation, and second, because that variable should probably not be used for the purpose of passing to Brittany, given the NOTE in the description.

Running into the same problem on emacs 26.1 using haskell-ide-engine built from source (commit 2d8f1b56). The emacs variable tab-width is set to 2.

Example code before formatting:

typeOf (TmCons t0 t1)
  | ty1 == TyList ty0 = ty1
  | otherwise         = error "type error for cons"
  where
    ty0 = typeOf t0
    ty1 = typeOf t1
typeOf (TmApp _t0 t1) = typeOf t1
typeOf (TmIsNil nt t) | nt /= ty = error "isNil type and t type do not match"
                      | otherwise = case ty of
                                      TyList _ -> TyBool
                                      _        -> error "isNil applied to something that isn't a List"
                      where ty = typeOf t

After formatting (apologies for not snagging the original version of TmApp and TmIsNil). The interesting thing is that TmCons uses a different tab width than TmIsNil.

typeOf (TmCons t0 t1) | ty1 == TyList ty0 = ty1
                      | otherwise         = error "type error for cons"
 where
  ty0 = typeOf t0
  ty1 = typeOf t1
typeOf (TmApp _t0 t1) = typeOf t1
typeOf (TmIsNil nt t)
  | nt /= ty = error "isNil type and t type do not match"
  | otherwise = case ty of
    TyList _ -> TyBool
    _        -> error "isNil applied to something that isn't a List"
  where ty = typeOf t

My brittany config.yaml looks like:

conf_debug:
  dconf_roundtrip_exactprint_only: false
  dconf_dump_bridoc_simpl_par: false
  dconf_dump_ast_unknown: false
  dconf_dump_bridoc_simpl_floating: false
  dconf_dump_config: false
  dconf_dump_bridoc_raw: false
  dconf_dump_bridoc_final: false
  dconf_dump_bridoc_simpl_alt: false
  dconf_dump_bridoc_simpl_indent: false
  dconf_dump_annotations: false
  dconf_dump_bridoc_simpl_columns: false
  dconf_dump_ast_full: false
conf_forward:
  options_ghc: []
conf_errorHandling:
  econf_ExactPrintFallback: ExactPrintFallbackModeInline
  econf_Werror: false
  econf_omit_output_valid_check: false
  econf_produceOutputOnErrors: false
conf_preprocessor:
  ppconf_CPPMode: CPPModeAbort
  ppconf_hackAroundIncludes: false
conf_obfuscate: false
conf_roundtrip_exactprint_only: false
conf_version: 1
conf_layout:
  lconfig_reformatModulePreamble: true
  lconfig_altChooser:
    tag: AltChooserBoundedSearch
    contents: 3
  lconfig_allowSingleLineExportList: false
  lconfig_importColumn: 50
  lconfig_hangingTypeSignature: false
  lconfig_importAsColumn: 50
  lconfig_alignmentLimit: 30
  lconfig_indentListSpecial: true
  lconfig_indentAmount: 2
  lconfig_alignmentBreakOnMultiline: true
  lconfig_cols: 100
  lconfig_indentPolicy: IndentPolicyFree
  lconfig_indentWhereSpecial: true
  lconfig_columnAlignMode:
    tag: ColumnAlignModeMajority
    contents: 0.7

Had the same problem with Emacs 26.2 and HIE on commit 0f697c8919747effca54be8a9b494896aea7d947

Fixed by adding this to the config:

(add-hook 'haskell-mode-hook (lambda ()  (setq tab-width 2)))

haskell-mode's indentation sets the tab-width to 8 locally overwriting any previous customisations.

Brittany honours the FormattingOptions given by the LSP while Floskell does not. This may indeed be the problem.
If this is the case, we could ignore FormattingOptions for Brittany as well, or just live with it and document it, or use the FormattingOptions for Brittany iff no explicit configuration is set. Maybe it is possible to merge default options, with LSP FormattingOptions already set, with configuration read from a file.

Was this page helpful?
0 / 5 - 0 ratings