reasonml / reasonml/reason

Attributes and other expressions add single space as identation

Open
#2,675 1 comment 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

Printer
Dominant language
OCaml
Stars
10.3k
Forks
438
PR merge metrics
No merged PRs in 30d

Description

There are a few cases where single space is added as indentation, I’m not entirely sure if it’s on purpose but it looks like a bug. Some of the cases I have found are:

  • Expressions inside JSX + ternary

         <Visibility hidden=loading>
            {iconPosition == `Left
               ? {
                 switch (icon) {
                 | Some(ico) =>
                   <Spacer right=2>
                     <Stack align=`Center distribute=`Center> ico </Stack>
                   </Spacer>
                 | None => React.null
                 };
               }
               : React.null}
    
  • Expressions inside JSX + switch (happens on the catch but as well on the branches)

    [@react.component]
    let make =
        (
          ~isDarkScreenEditorFlow,
          ~toPreview,
          ~searchQuery="",
          ~showBlank,
          ~className=?,
        ) => {
      let templateListQuery = Template.GQL.List.useQuery();
    
      <ul
        className={Cn.make([
          "flex flex-row flex-wrap",
          className->Cn.unpack->Cn.ifSome(className),
        ])}>
        {switch (templateListQuery) {
         | NoData
         | Loading =>
           Belt.Array.rangeBy(0, 6, ~step=1)
           ->Utils.React.mapArray((_, _) => <TemplateListItem.Loading />)
         | Error(e) =>
           Sentry.captureException(e);
           React.null;
         | Data(allTemplates) =>
           let (blank, others) =
             allTemplates->Belt.Array.partition(t => t.name === "Blank");
           let unfilteredTemplates =
             showBlank ? blank->Js.Array2.concat(others) : others;
           let filteredTemplates =
             unfilteredTemplates->Js.Array2.filter(t =>
               t.name
               ->Js.String.toLowerCase
               ->Js.String2.includes(searchQuery->Js.String.toLowerCase)
             );
    
           filteredTemplates
           ->Js.Array2.map(t =>
               <TemplateListItem
                 key={t.uuid}
                 uuid={t.uuid}
                 name={t.name}
                 imageUrl={t.imageUrl}
                 visible={t.visible}
                 toPreview
                 isDarkScreenEditorFlow
               />
             )
           ->React.array;
         }}
      </ul>;
    };
    
    let default = make;
    
  • Expressions inside JSX + switch (happens on the match_expr)

    <GlobalContext.Provider>
        {switch (
           tokenRefreshed,
           Utils.Apollo.Query.combineResultsTuple2(screenQuery, appQuery),
         ) {
         | (_, Data((screen, app))) =>
    
  • Expressions with pipe and callback

    let handleSplashScreenChange = (file: S3.file) => {
        S3.uploadFile({
          "file": file,
          "appUuid": Some(uuid),
          "workspaceUuid": None,
          "mimeType": file##type__,
        })
        |> Js.Promise.then_(url => {
             dispatch(UpdateSplashScreen(Some(url)));
             Analytics.track("Uploaded Splash Screen");
             Js.Promise.resolve();
           });
      };
    
    let extractErrorMessage = err => {
      let errorsJson: Js.Json.t = Obj.magic(err);
    
      (
        errorsJson
        |> Json.Decode.(
             oneOf([
               j =>
                 (j |> array(field("errors", array(field("message", string)))))
                 ->ArrayUtils.flatten,
               field("graphQLErrors", array(field("message", string))),
             ])
           )
      )
      ->Belt.Array.get(0);
    };
    
  • Expression inside JSX being a function that returns a component (the props)

    [@react.component]
    let make = (~icon: NavigationT.iconOptions, ~label="Text & Icon", ~onChange) => {
      <Control.NavigationReset
        label
        value={icon.color}
        defaultValue={NavigatorConfigParser.defaultIconOptions.color}
        onChange={color => onChange({...icon, color})}>
        {(value, onChange) =>
           <SelectThemeColor.App
             name="iconColor"
             value={value->Belt.Option.getWithDefault("")}
             onChange={color => onChange(color)}
           />}
      </Control.NavigationReset>;
    };
    

Contributor guide

No contributing guide indexed for this repository

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start by reproducing the extra single-space indentation in each OCaml/JSX, switch, pipe, callback, and component-prop example from the issue. Trace the formatter entry point that handles these expressions, then verify that formatting preserves the intended nesting without adding the stray space across all listed cases.

Written by the indexing model from the issue text.

Assessment

Tech stack
ocaml
Domain
tooling
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.