profile
viewpoint
If you are wondering where the data of this site comes from, please visit https://api.github.com/users/msaraiva/events. GitMemory does not store any data, but only uses NGINX to cache data for a period of time. The idea behind GitMemory is simply to give users a better reading experience.

elixir-lsp/elixir-ls 743

A frontend-independent IDE "smartness" server for Elixir. Implements the "Language Server Protocol" standard and provides debugger support via the "Debug Adapter Protocol"

msaraiva/atom-elixir 418

An Atom package for Elixir

elixir-lsp/vscode-elixir-ls 331

Elixir language support and debugger for VS Code, powered by ElixirLS.

msaraiva/alpine-erlang 209

Minimal Erlang/Elixir docker images with Alpine Linux

elixir-lsp/elixir_sense 189

Provides context-aware information for code completion, documentation, go/jump to definition, signature info and more

msaraiva/elixir_sense 185

Provides context-aware information for code completion, documentation, go/jump to definition, signature info and more

dashbitco/broadway_kafka 119

A Broadway connector for Kafka

msaraiva/exsamples 110

Initializes lists of maps, structs or keyword lists using tabular data in Elixir

msaraiva/surface_app 12

A bare bones Surface boilerplate without any node/npm dependencies

push eventphoenixframework/phoenix_live_view

Marlus Saraiva

commit sha c303746740d46c13509b2b4ab4e799b26e9de748

Make it explicit it's the end position in :text meta

view details

push time in 15 hours

PullRequestReviewEvent

pull request commentsurface-ui/surface

Fix directives :if with :values

@Malian that's a good point. I believe after we remove all the already deprecated directives like :for, :attrs. :props, etc., :if will be the only one that replaces the original node so this should not be an issue for a while. However, I think we should rethink the way we handle directives as a whole as soon as we have a chance.

miguel-s

comment created time in a day

push eventsurface-ui/surface

Hubert Łępicki

commit sha 0d20f016612834c592a65d1502601d0f327c0830

[#481] Disable build timestamp, replace header (#490) This change removes the build timestamp from headers of the generated files. This change also improves the tests by generating the hook files in the tests themselves, making sure their content changes and not just rely on the timestamps / build times.

view details

push time in 4 days

PR merged surface-ui/surface

[#481] Disable build timestamp, replace header

This change removes the build timestamp from headers of the generated files, and replaces it with a helpful message pointing to the original source files that should be modified instead of the current file.

This change also improves the tests by generating the hook files in the tests themselves, making sure their content changes and not just rely on the timestamps / build times.

Closes #481

+39 -41

1 comment

5 changed files

hubertlepicki

pr closed time in 4 days

issue closedsurface-ui/surface

Can we disable build time timestamp?

I would like to add a config option to Surface that would disable adding "Build time: " line. The code in question is here:

https://github.com/surface-ui/surface/blob/6164224a7b156dd41327db7142af7eae182e86bb/lib/mix/tasks/compile/surface.ex#L176

The reasoning is that it annoys me when switching branches and is being added to commits by developers by mistake, even if nothing changes, resulting in merge conflicts that have to be resolved.

I can provide a PR.

That is if you want to keep this. I am not sure this timestamp is needed at all - the file on the filesystem can be also queried for modification date, so maybe we don't need it at all and can just rely on that? If that's preferred I'd remove the line completely.

closed time in 4 days

hubertlepicki
PullRequestEvent

pull request commentsurface-ui/surface

[#481] Disable build timestamp, replace header

@hubertlepicki thanks! ❤️

hubertlepicki

comment created time in 4 days

PR closed surface-ui/surface

[#481] Disable build timestamp, replace header

This change removes the build timestamp from headers of the generated files, and replaces it with a helpful message pointing to the original source files that should be modified instead of the current file.

This change also improves the tests by generating the hook files in the tests themselves, making sure their content changes and not just rely on the timestamps / build times.

Closes #481

+39 -41

0 comment

5 changed files

hubertlepicki

pr closed time in 4 days

issue commentsurface-ui/surface

Can we disable build time timestamp?

Hey @hubertlepicki!

I just review it and it looks great!

At the same time, I figured the message in header files could be a bit more helpful and point the user to the source files, which I added.

Maybe we don't need to expose the source file path at all (even if it's just the relative path). I mean, the file name is already the full module name of the component, so it should be straightforward to the user to find the source. The less information we generate the better :)

Let me know what you think so I can merge it.

Thanks a bunch for your contribution!

hubertlepicki

comment created time in 5 days

pull request commentsurface-ui/surface_site

:pencil: Write simple integration guide for Getting Started page (Resolves #37)

@justin-m-morgan thanks for the PR! That's a great addition to the docs.

I'll put this on hold for a few days, just until we have the mix surface.init task finished as it will likely be able to apply some of changes you mentioned by default and some others using additional options like --with-sample-component. As soon as it's available, we can review the text and see if it needs any adjustments.

Thanks for your contribution! ❤️

justin-m-morgan

comment created time in 7 days

pull request commentsurface-ui/surface

Add placeholder prop to supporting form inputs (#446)

@Malian IMO that would add a lot of nice DX compared to only having the opts prop. I remember that @msaraiva had some reservations about this approach; iirc his concern was around having bloated components, but I can't find our conversation. @msaraiva do I remember correctly?

The main issue is about user expectations. If we start defining all available attributes as props to some components, we'll have to do it all of them, otherwise the users will always be in doubt if an attribute is defined as a prop or not, making the general usage of components less intuitive. The way we try to overcome that is by defining a simple easy-to-remember rule. Currently, the rule is that we only define a prop for an existing HTML attribute if:

  1. The attribute is specific to the related HTML element. For instance, the <RangeInput> defines min, max and step as those are not the generic global attributes.
  2. We need to add some extra behaviour for that attribute, e.g. add validation (type check or if it's required), define a default value, etc.

Those a the most basic rules we could come up with and, although it's not perfect, it's still much more intuitive them randomly adding props to some components. What we could do, however, as middle ground solution for form controls, is to redefine the first statement as something like:

  1. The attribute is specific to the related HTML element or a category of elements (e.g. inputs). For instance, the <RangeInput> defines min, max and step (as those are not the generic global attributes) and the value attribute, which is defined for every input.

In this case, I'm not sure which category the placeholder attribute belongs to. Is it available for all inputs? If not, can we identify all components that have that attribute as a homogeneous group (category)? Like text-based inputs or whatever.

If so, I think we can certainly move on in that direction and add not only placeholder but all attributes belonging to that same identifiable group, otherwise, we should probably wait a bit until we introduce the concept of "interfaces", which will make it easier to define/share groups of props declarations.

justin-m-morgan

comment created time in 7 days

issue closeddoorgan/sourceror

Sourceror.get_range/1 raises for keyword list without brackets

Hi @doorgan!

I recently run into this problem when using get_range/1.

Here's a minimal example:

alias Sourceror.Zipper, as: Z

code = 
  """
  config :my_app, :some_key,
    value: [
      a: 1
    ]
  """

last_arg_node = code |> Sourceror.parse_string!() |> Z.zip() |> Z.down |> Z.rightmost() |> Z.node()

Printing the node works fine:

iex> last_arg_node |> Sourceror.to_string(format: :splicing) |> IO.puts

  value: [
    a: 1
  ]

:ok

So I guess I have the correct node. However, if I tried to get the range with:

Sourceror.get_range(last_arg_node)

This raises:

** (FunctionClauseError) no function clause matching in Sourceror.Range.do_get_range/1    
    
    The following arguments were given to Sourceror.Range.do_get_range/1:
    
        # 1
        [
          {{:__block__,
            [
              trailing_comments: [],
              leading_comments: [],
              format: :keyword,
              line: 2,
              column: 3
            ], [:value]},
           {:__block__,
            [ 
              trailing_comments: [],
              leading_comments: [],
              newlines: 1,
              closing: [line: 4, column: 3],
              line: 2,
              column: 10
            ],
            [
              [
                {{:__block__,
                  [
                    trailing_comments: [],
                    leading_comments: [],
                    format: :keyword,
                    line: 3,
                    column: 5
                  ], [:a]},
                 {:__block__,
                  [
                    trailing_comments: [],
                    leading_comments: [],
                    token: "1",
                    line: 3,
                    column: 8
                  ], [1]}}
              ]
            ]}}
        ]
    
    Attempted function clauses (showing 10 out of 18):
    
        defp do_get_range({:__aliases__, meta, segments})
        defp do_get_range({:__block__, meta, [string]}) when is_binary(string)
        defp do_get_range({:__block__, meta, [number]}) when is_integer(number) or is_float(number)
        defp do_get_range({:__block__, meta, [atom]}) when is_atom(atom)
        defp do_get_range({:__block__, _, args} = quoted)
        defp do_get_range({form, meta, context}) when is_atom(form) and is_atom(context)
        defp do_get_range({left, right})
        defp do_get_range({{:., _, [Access, :get]}, _, _} = quoted)
        defp do_get_range({{:., _, [_, :{}]}, _, _} = quoted)
        defp do_get_range({{:., _, [:erlang, :binary_to_atom]}, meta, [interpolation, :utf8]})
        ...
        (8 clauses not shown)
    
    (sourceror 0.8.2) lib/sourceror/range.ex:50: Sourceror.Range.do_get_range/1
    (sourceror 0.8.2) lib/sourceror/range.ex:11: Sourceror.Range.get_range/2

If the keyword list is wrapped in brackets, like:

  config :my_app, :some_key, [
    value: [
      a: 1
    ]
  ]

It works just fine. I'm I doing something wrong?

closed time in 7 days

msaraiva

issue commentdoorgan/sourceror

Sourceror.get_range/1 raises for keyword list without brackets

This means that if you alter the tree, you can: a) get incorrect numbers b) not be able to get them at all(because they don't have such metadata)

Yeah, that makes sense.

Also at this stage this is more about making it possible than making it as efficient as possible :p Parsing, reparsing, traversing and retraversing is definitely not cheap in big projects

For my use cases that wouldn't be an issue as I just need to update a few files when bootstrapping a fresh project. I'll try this approach later. Maybe creating a higher lever abstraction that can do the following in a nice way:

  1. Parse the code (and keep it for later use - patching!)
  2. Use zippers to make it easier to find nodes and register patches for them
  3. Apply all registered patches one by one, always re-parsing the previously patched code before applying the next patch

I'll let you know, if I can get anything done :)

Thanks!

msaraiva

comment created time in 7 days

issue commentdoorgan/sourceror

Sourceror.get_range/1 raises for keyword list without brackets

Hey @doorgan!

Thanks for the quick reply.

I tested that code using v0.8.3 and it works fine, however, it seems there's still a related issue when we try to manipulate the tree.

Here's an example:

alias Sourceror.Zipper, as: Z

code = "config :my_app, :some_key, value1: 1"

root_node = Sourceror.parse_string!(code)
new_option = {{:__block__, [format: :keyword], [:value2]}, {:__block__, [token: "2"], [2]}}
opts_zipper = root_node |> Z.zip() |> Z.down |> Z.rightmost()
updated_root_node = opts_zipper |> Z.append_child(new_option) |> Z.root()

# The new code looks fine
updated_root_node |> Sourceror.to_string() |> IO.puts

# But this raises
Sourceror.get_range(updated_root_node)
msaraiva

comment created time in 8 days

issue openeddoorgan/sourceror

Sourceror.get_range/1 raises for keyword list without brackets

Hi @doorgan!

I recently run into this problem when using get_range/1.

Here's a minimal example:

alias Sourceror.Zipper, as: Z

code = 
  """
  config :my_app, :some_key,
    value: [
      a: 1
    ]
  """

last_arg_node = code |> Sourceror.parse_string!() |> Z.zip() |> Z.down |> Z.rightmost() |> Z.node()

Printing the node works fine:

iex> last_arg_node |> Sourceror.to_string(format: :splicing) |> IO.puts

  value: [
    a: 1
  ]

:ok

So I guess I have the correct node. However, if I tried to get the range with:

Sourceror.get_range(last_arg_node)

This raises:

** (FunctionClauseError) no function clause matching in Sourceror.Range.do_get_range/1    
    
    The following arguments were given to Sourceror.Range.do_get_range/1:
    
        # 1
        [
          {{:__block__,
            [
              trailing_comments: [],
              leading_comments: [],
              format: :keyword,
              line: 2,
              column: 3
            ], [:value]},
           {:__block__,
            [ 
              trailing_comments: [],
              leading_comments: [],
              newlines: 1,
              closing: [line: 4, column: 3],
              line: 2,
              column: 10
            ],
            [
              [
                {{:__block__,
                  [
                    trailing_comments: [],
                    leading_comments: [],
                    format: :keyword,
                    line: 3,
                    column: 5
                  ], [:a]},
                 {:__block__,
                  [
                    trailing_comments: [],
                    leading_comments: [],
                    token: "1",
                    line: 3,
                    column: 8
                  ], [1]}}
              ]
            ]}}
        ]
    
    Attempted function clauses (showing 10 out of 18):
    
        defp do_get_range({:__aliases__, meta, segments})
        defp do_get_range({:__block__, meta, [string]}) when is_binary(string)
        defp do_get_range({:__block__, meta, [number]}) when is_integer(number) or is_float(number)
        defp do_get_range({:__block__, meta, [atom]}) when is_atom(atom)
        defp do_get_range({:__block__, _, args} = quoted)
        defp do_get_range({form, meta, context}) when is_atom(form) and is_atom(context)
        defp do_get_range({left, right})
        defp do_get_range({{:., _, [Access, :get]}, _, _} = quoted)
        defp do_get_range({{:., _, [_, :{}]}, _, _} = quoted)
        defp do_get_range({{:., _, [:erlang, :binary_to_atom]}, meta, [interpolation, :utf8]})
        ...
        (8 clauses not shown)
    
    (sourceror 0.8.2) lib/sourceror/range.ex:50: Sourceror.Range.do_get_range/1
    (sourceror 0.8.2) lib/sourceror/range.ex:11: Sourceror.Range.get_range/2

If the keyword list is wrapped in brackets, like:

  config :my_app, :some_key, [
    value: [
      a: 1
    ]
  ]

It works just fine. I'm I doing something wrong?

created time in 8 days

issue commentsurface-ui/surface

Surface.LiveView does not render configured layout

Hi @justin-m-morgan!

Yes. Either you create a new surface_live_view macro (as you did) or you change the original live_view to use Surface.LiveView.

I agree we should add a note to the docs. Maybe in the Getting Started guide as an "optional" step. Feel free to submit a PR if you want.

justin-m-morgan

comment created time in 13 days

issue commentsurface-ui/surface

Improve documentation on Surface.Component vs Surface.LiveComponent for master branch

Hi @thorsten-de!

Yes. The mount and update callbacks are no longer available as Surface.Component now is built on top of function components (instead of stateless live components). We'll review/update the docs and add a new "Migrating from v0.5.x to v0.6.x" section in our Migration Guide as soon as v0.6 is ready.

Basically, any data preparation that was done inside those callbacks must be moved to render/1. The API has been updated so you can use assign, assign_new, etc. in any function component.

Here's an example from our tests where we adjusted the code: https://github.com/surface-ui/surface/commit/514bbbda4575378d45e3788a3f0391e8d0581a60#diff-13af13c1f87d799c73dbe03fb8f944c40bb68f6b4b14f7cdab105d160fe7fdddL108-R115

In the meantime, feel free to submit a PR to improve the docs or the website. Any help is certainly appreciated.

Cheers.

thorsten-de

comment created time in 13 days

issue commentphoenixframework/phoenix_live_view

Support Slots

I would skip the <#slot name="header" /> syntax on the initial version

I agree.

What are you thinking here? Something like this?

[%{name: atom, assigns: map, inner_block: fun.() | nil}]

Yeah. Something like that. Not sure if using a tuple instead of a map would be better.

msaraiva

comment created time in 13 days

issue openedphoenixframework/phoenix_live_view

Support Slots

Initial proposal to bring slots to LV components.

Motivation

Slots provide a standard way to pass blocks of templates to components, improving composability. The blocks can also hold additional information (slot props) that can be used by the component to customize the rendering.

The concept is similar to component properties as it defines a way to pass information to a component using its public API. The main difference is that slots can only be assigned as direct children of the component instead of attributes.

Example (Surface)

The component definition:

defmodule Card do
  use Surface.Component

  slot header
  slot footer
  slot default, required: true

  def render(assigns) do
    ~F"""
    <div class="card">
      <header class="card-header">
        <p class="card-header-title">
          <#slot name="header" />
        </p>
      </header>
      <div class="card-content">
        <div class="content">
          <#slot />
        </div>
      </div>
      <footer class="card-footer">
        <#slot name="footer" />
      </footer>
    </div>
    """
  end
end

Usage:

<Card>
  <:header>
    A simple card component
  </:header>

  This example demonstrates how to create components with multiple slots.
  It defines a <strong>default</strong> slot to hold the card's content
  and two <strong>named slots</strong>: header and footer.

  <:footer>
    <a href="#" class="card-footer-item">Footer Item 1</a>
    <a href="#" class="card-footer-item">Footer Item 2</a>
  </:footer>
</Card>

Current implementation

Since there's no built-in support for slots in LV, Surface's implementation depends on pattern matching the arguments passed by render_block/2. Something like the following (in HEEx):

<%= component &Card.render/1 do %>
  <% {:default, 0, _args} -> %>
    This example...

  <% {:header, 0, _args} -> %>
    A simple card component

  <% {:footer, 0, _args} -> %>
    <a href="#" class="card-footer-item">Footer Item 1</a>
    ...
<% end %>

The assigned slot item is represented as {name, index, args} where name is the name of the slot, index is the index of the assigned block (as it accepts multiple blocks for the same slot) and args are the values passed as the second argument of render_block/2.

Limitation

One limitation of this approach it that, in order to make the change tracker work nicely, the clauses representing each slot item must be generated at compile-time, that means the user can't assign slots dynamically, for instance using a for comprehension.

By having slots as a built-in feature with a standard API we can improve composability and provide better ergonomics to HEEx. It would also not on only keep change tracking working but it would allow further optimizations as the engine will be aware of the slots API.

Solution

The standard API could implement slots as regular assigns (probably a list of maps) where each map in that list would represent a single assigned slot item. Each item should hold the passed values of the slot props along with a inner_block field containing the anonymous function representing the inner block of that slot item. Each inner block could be rendered using an updated version of render_block or something similar.

On top of that, we should extend the HTML tokenizer/engine to accept a custom syntax to assign slots. The syntax could be the same as the Surface's syntax or whatever the core team think is better for HEEx. As long as the underlying model covers all features, the syntax is just a matter of taste.

More info

  • https://surface-ui.org/slots
  • https://developer.mozilla.org/en-US/docs/Web/Web_Components/Using_templates_and_slots
  • https://v3.vuejs.org/guide/component-slots.html

created time in 13 days

delete branch msaraiva/phoenix_live_view

delete branch : handle-self-close-tags

delete time in 13 days

issue commentsurface-ui/surface

how to escape <> or {} in template

You can either use HTML entities:

  • < -> &lt;
  • > -> &gt;
  • { -> &lcub;
  • } -> &rcub;

or just wrap than in {""}:

<SomeComponent>
   some text that contains {"<>"}  {"{}"} 
</SomeComponent>
schneiderlin

comment created time in 14 days

issue closedsurface-ui/surface

how to escape <> or {} in template

I might be missing something obvious, but I don't see a document about how to write <> or {} inside tag, like this.

<SomeComponent>
   some text that contains <> {}
</SomeComponent>

thanks in advance

closed time in 14 days

schneiderlin

create barnchmsaraiva/phoenix_live_view

branch : handle-self-close-tags

created branch time in 14 days

delete branch msaraiva/phoenix_live_view

delete branch : relax-html-tag-validation

delete time in 14 days

PR opened phoenixframework/phoenix_live_view

Relax html tag validation

Otherwise the tokenizer emits a warning for things like:

<svg>
  ...
    <linearGradient ...>
      ...
    </linearGradient>
  ...
</svg>
+1 -34

0 comment

2 changed files

pr created time in 14 days

create barnchmsaraiva/phoenix_live_view

branch : relax-html-tag-validation

created branch time in 14 days

startedsurface-ui/surface

started time in 15 days

delete branch surface-ui/surface

delete branch : ms-function-component

delete time in 15 days