Blog

  • graal-sqs-example

    graal-sqs-example

    An example of an application that reads from Amazon SQS built as a native image with GraalVM.

    Prerequisites

    This example requires that you have GraalVM installed on your system.

    Building the Example

    Run the following command to build the example application as both a JAR distribution and a native image:

    ./gradlew buildAll
    

    Running the Example

    Follow the steps below to run the example:

    1. Run the following command to start a mock AWS environment using LocalStack:

        ./gradlew startLocalStack
      
    2. Run the following command to execute the application as an Uber Jar on the JVM:

        ./run-uber-jar.sh
      

      If successful, you will see the following in the terminal:

        Connecting to SQS: http://localhost:4566 [us-east-1]
        SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
        SLF4J: Defaulting to no-operation (NOP) logger implementation
        SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
        Checking for messages...
        No messages found... sleeping
      
    3. In a new terminal window, run the following command to start the application as a native image:

        ./run-native-image.sh
      

      If successful, you will see the following in the terminal:

        Connecting to SQS: http://localhost:4566 [us-east-1]
        Checking for messages...
        No messages found... sleeping
      

      Notice that you do not see the SLF4J missing configuration error. This is because the reflection has already occurred at compile time.

    4. In a new terminal window, run the following command to begin publishing messages to the queue for the readers to receive:

        ./gradlew publishMessages
      

      If successful, you will see both readers printing messages in their terminals:

        Checking for messages...
        message-872
        message-873
        message-874
        message-875
        message-876
        Checking for messages...
      

    Visit original content creator repository
    https://github.com/gregwhitaker/graal-sqs-example

  • graal-sqs-example

    graal-sqs-example

    An example of an application that reads from Amazon SQS built as a native image with GraalVM.

    Prerequisites

    This example requires that you have GraalVM installed on your system.

    Building the Example

    Run the following command to build the example application as both a JAR distribution and a native image:

    ./gradlew buildAll
    

    Running the Example

    Follow the steps below to run the example:

    1. Run the following command to start a mock AWS environment using LocalStack:

        ./gradlew startLocalStack
      
    2. Run the following command to execute the application as an Uber Jar on the JVM:

        ./run-uber-jar.sh
      

      If successful, you will see the following in the terminal:

        Connecting to SQS: http://localhost:4566 [us-east-1]
        SLF4J: Failed to load class "org.slf4j.impl.StaticLoggerBinder".
        SLF4J: Defaulting to no-operation (NOP) logger implementation
        SLF4J: See http://www.slf4j.org/codes.html#StaticLoggerBinder for further details.
        Checking for messages...
        No messages found... sleeping
      
    3. In a new terminal window, run the following command to start the application as a native image:

        ./run-native-image.sh
      

      If successful, you will see the following in the terminal:

        Connecting to SQS: http://localhost:4566 [us-east-1]
        Checking for messages...
        No messages found... sleeping
      

      Notice that you do not see the SLF4J missing configuration error. This is because the reflection has already occurred at compile time.

    4. In a new terminal window, run the following command to begin publishing messages to the queue for the readers to receive:

        ./gradlew publishMessages
      

      If successful, you will see both readers printing messages in their terminals:

        Checking for messages...
        message-872
        message-873
        message-874
        message-875
        message-876
        Checking for messages...
      

    Visit original content creator repository
    https://github.com/gregwhitaker/graal-sqs-example

  • minecraft-sign-shop

    Good morning, good afternoon and good night!

    Today I’m bringing for the first time a very simple store plugin for Minecraft servers.

    The cmLoja consists of creating stores using signs, such as the ChestShop plugin.

    Both staff and players can create stores, so just have the permissions!

    ✦ Test the versions: 1.13x, 1.12x, 1.11x, 1.10x, 1.9x, 1.8x

    ✦ Plugin Video: https://youtu.be/Dklc07uynfg

    ✦ Commands: /geraritem

    ➤ Generates some custom item to add on sale using the sign.

    ✦ Permissions ✦

    loja.geraritem: Permission to use the /geraritem command

    loja.admin: Permission for admin to create store of all kinds.

    loja.player: Permission for the player to create store using the chest.

    loja.quebrarloja: Permission for the player to be able to break any Vip store.

    loja.abrirbau: Permission for the player to be able to open the chest of any VIP store.

    ✦ VIP Discount ✦

    ➤ loja.vender.0-100: Permission to give “discount” in the store, that is, the player will sell more expensive items only in the server store. The values can be from 0 to 100 per cent.

    ➤ loja.comprar.0-100: Permission to discount the store when buying any items in the server store.

    ➤ Example: loja.comprar.10 <- The player will have 10% discount on the server store.

    ➤ ADMINS DO NOT HAVE DISCOUNTS IN SHOP, THAT IS, NO OF THE 2 PERMISSIONS WILL WORK.

    ✦ Messages Config: https://pastebin.com/T2bkg1g7

    ✦ Events for programmers ✦

    LojaBuyOtherPlayer – It is always called when a player buys in another player’s store.

    LojaBuyServer – It is called whenever any player buys in the server store.

    LojaSellOtherPlayer – It is called whenever a player sells items in another player’s store.

    LojaSellServer – It is called whenever a player sells items in the server store.

    LojaSignCreate – It is called whenever a player creates a store.

    ♦ Important Notices ♦

    The plugin needs Vault and some economy plugin on the server.

    If you find errors in the plugin let me know that I will edit to launch in the next version.

    Visit original content creator repository
    https://github.com/cassiompf/minecraft-sign-shop

  • thinking-in-redux

    My Thinking in Redux

    Thinking in Redux

    My production tested implementation of a React with Redux action and effect design pattern based on the book Thinking in Redux by Nir Kaufman.

    Pictures below are from his book :).

    Redux Data Flow

    Redux Data Flow

    Action Flow

    Action Flow

    Middleware Action Routers

    Action Flow


    This project was bootstrapped with Create React App.

    Available Scripts

    In the project directory, you can run:

    npm start

    Runs the app in the development mode.
    Open http://localhost:3000 to view it in the browser.

    The page will reload if you make edits.
    You will also see any lint errors in the console.

    npm test

    Launches the test runner in the interactive watch mode.
    See the section about running tests for more information.

    npm run build

    Builds the app for production to the build folder.
    It correctly bundles React in production mode and optimizes the build for the best performance.

    The build is minified and the filenames include the hashes.
    Your app is ready to be deployed!

    See the section about deployment for more information.

    npm run eject

    Note: this is a one-way operation. Once you eject, you can’t go back!

    If you aren’t satisfied with the build tool and configuration choices, you can eject at any time. This command will remove the single build dependency from your project.

    Instead, it will copy all the configuration files and the transitive dependencies (webpack, Babel, ESLint, etc) right into your project so you have full control over them. All of the commands except eject will still work, but they will point to the copied scripts so you can tweak them. At this point you’re on your own.

    You don’t have to ever use eject. The curated feature set is suitable for small and middle deployments, and you shouldn’t feel obligated to use this feature. However we understand that this tool wouldn’t be useful if you couldn’t customize it when you are ready for it.

    Learn More

    You can learn more in the Create React App documentation.

    To learn React, check out the React documentation.

    Code Splitting

    This section has moved here: https://facebook.github.io/create-react-app/docs/code-splitting

    Analyzing the Bundle Size

    This section has moved here: https://facebook.github.io/create-react-app/docs/analyzing-the-bundle-size

    Making a Progressive Web App

    This section has moved here: https://facebook.github.io/create-react-app/docs/making-a-progressive-web-app

    Advanced Configuration

    This section has moved here: https://facebook.github.io/create-react-app/docs/advanced-configuration

    Deployment

    This section has moved here: https://facebook.github.io/create-react-app/docs/deployment

    npm run build fails to minify

    This section has moved here: https://facebook.github.io/create-react-app/docs/troubleshooting#npm-run-build-fails-to-minify

    Visit original content creator repository https://github.com/joemar-tagpuno/thinking-in-redux
  • vuex-connect

    vuex-connect

    npm version Build Status vuex-connect Dev Token

    A binding utility for a Vue component and a Vuex store.
    Inspired by react-redux‘s connect function.

    Example

    First, create a Vue component. This component can communicate to a parent component using events and recieve data from the parent through props.

    // hello-component.js
    export default {
      props: {
        message: {
          type: String,
          required: true
        }
      },
      methods: {
        updateMessage(event) {
          this.$emit('update', event.target.value)
        }
      },
      template: `
      <div>
        <p>{{ message }}</p>
        <input type="text" :value="message" @input="updateMessage">
      </div>
      `
    }

    You can bind the component and the Vuex store by vuex-connect.
    The connect function wraps the component, returning a new wrapper component.

    import { connect } from 'vuex-connect'
    import HelloComponent from './hello-component'
    
    export default connect({
      stateToProps: {
        message: state => state.message
      },
    
      methodsToEvents: {
        update: ({ commit }, value) => commit('UPDATE_INPUT', value)
      },
    
      lifecycle: {
        mounted: ({ commit }) => {
          fetch(URL)
            .then(res => res.text())
            .then(value => commit('UPDATE_INPUT', value));
        }
      }
    })('hello', HelloComponent)

    You can use getters, actions and mutations if you define them in your store.

    import { connect } from 'vuex-connect'
    import HelloComponent from './hello-component'
    
    export default connect({
      gettersToProps: {
        message: 'inputMessage' // 'prop name': 'getter name'
      },
    
      mutationsToEvents: {
        update: 'UPDATE_INPUT' // 'event name': 'mutation type'
      },
    
      lifecycle: {
        mounted: store => store.dispatch('FETCH_INPUT', URL)
      }
    })('hello', HelloComponent)

    API

    connect(options) -> (componentName, Component) -> WrapperComponent

    • options: Object
      • stateToProps
      • gettersToProps
      • actionsToProps
      • actionsToEvents
      • mutationsToProps
      • mutationsToEvents
      • methodsToProps
      • methodsToEvents
      • lifecycle
    • componentName: string
    • Component: Vue component or component option
    • WrapperComponent: Vue component

    Connects a Vue component to a Vuex store.

    stateToProps, gettersToProps, actionsTo(Props|Events) and mutationsTo(Props|Events) have the same interface as Vuex’s mapState, mapGetters, mapActions and mapMutations. In addition, you can define inline methods by using methodsTo(Props|Events).

    The options suffixed by Props indicate that they will be passed to the wrapped component’s props. For example, the following option retrieves a store’s state via the inputMessage getter and passes it to the message prop of the wrapped component.

    connect({
      gettersToProps: {
        message: 'inputMessage'
      }
    })

    The options suffixed by Events indicate that they will listen to the wrapped component’s events. For example, the following option observes the update event of the wrapped component and if it is emitted, the UPDATE_INPUT mutation is committed.

    connect({
      mutationsToEvents: {
        update: 'UPDATE_INPUT'
      }
    })

    lifecycle is lifecycle hooks for a Vue component. The lifecycle hooks receive a Vuex store for their first argument. You can dispatch some actions or mutations in the lifecycle hooks.

    connect returns another function. The function expects a component name and the component constructor. The component name should be a string. It is useful to specify the component in the debug phase.

    createConnect(fn) -> ConnectFunction

    Creates a customized connect function. fn is transform function of the wrapper component options and will receive a component options object and a lifecycle option of the connect function. You may want to inject some additional lifecycle hooks in fn.

    Example of defining vue-router lifecycle hooks:

    // connect.js
    import { createConnect } from 'vuex-connect'
    
    export const connect = createConnect((options, lifecycle) => {
      options.beforeRouteEnter = lifecycle.beforeRouteEnter
    
      options.beforeRouteUpdate = function(to, from, next) {
        return lifecycle.beforeRouteUpdate.call(this, this.store, to, from, next)
      }
    
      options.beforeRouteLeave = function(to, from, next) {
        return lifecycle.beforeRouteLeave.call(this, this.store, to, from, next)
      }
    })

    It can be used as:

    import { connect } from './connect'
    import { Example } from './example'
    
    export default connect({
      lifecycle: {
        beforeRouteEnter(to, from, next) {
          // ...
        },
    
        beforeRouteUpdate(store, to, from, next) {
          // ...
        },
    
        beforeRouteLeave(store, to, from, next) {
          // ...
        }
      }
    })('example', Example)

    License

    MIT

    Visit original content creator repository https://github.com/ktsn/vuex-connect
  • vuex-connect

    vuex-connect

    npm version Build Status vuex-connect Dev Token

    A binding utility for a Vue component and a Vuex store.
    Inspired by react-redux‘s connect function.

    Example

    First, create a Vue component. This component can communicate to a parent component using events and recieve data from the parent through props.

    // hello-component.js
    export default {
      props: {
        message: {
          type: String,
          required: true
        }
      },
      methods: {
        updateMessage(event) {
          this.$emit('update', event.target.value)
        }
      },
      template: `
      <div>
        <p>{{ message }}</p>
        <input type="text" :value="message" @input="updateMessage">
      </div>
      `
    }

    You can bind the component and the Vuex store by vuex-connect.
    The connect function wraps the component, returning a new wrapper component.

    import { connect } from 'vuex-connect'
    import HelloComponent from './hello-component'
    
    export default connect({
      stateToProps: {
        message: state => state.message
      },
    
      methodsToEvents: {
        update: ({ commit }, value) => commit('UPDATE_INPUT', value)
      },
    
      lifecycle: {
        mounted: ({ commit }) => {
          fetch(URL)
            .then(res => res.text())
            .then(value => commit('UPDATE_INPUT', value));
        }
      }
    })('hello', HelloComponent)

    You can use getters, actions and mutations if you define them in your store.

    import { connect } from 'vuex-connect'
    import HelloComponent from './hello-component'
    
    export default connect({
      gettersToProps: {
        message: 'inputMessage' // 'prop name': 'getter name'
      },
    
      mutationsToEvents: {
        update: 'UPDATE_INPUT' // 'event name': 'mutation type'
      },
    
      lifecycle: {
        mounted: store => store.dispatch('FETCH_INPUT', URL)
      }
    })('hello', HelloComponent)

    API

    connect(options) -> (componentName, Component) -> WrapperComponent

    • options: Object
      • stateToProps
      • gettersToProps
      • actionsToProps
      • actionsToEvents
      • mutationsToProps
      • mutationsToEvents
      • methodsToProps
      • methodsToEvents
      • lifecycle
    • componentName: string
    • Component: Vue component or component option
    • WrapperComponent: Vue component

    Connects a Vue component to a Vuex store.

    stateToProps, gettersToProps, actionsTo(Props|Events) and mutationsTo(Props|Events) have the same interface as Vuex’s mapState, mapGetters, mapActions and mapMutations. In addition, you can define inline methods by using methodsTo(Props|Events).

    The options suffixed by Props indicate that they will be passed to the wrapped component’s props. For example, the following option retrieves a store’s state via the inputMessage getter and passes it to the message prop of the wrapped component.

    connect({
      gettersToProps: {
        message: 'inputMessage'
      }
    })

    The options suffixed by Events indicate that they will listen to the wrapped component’s events. For example, the following option observes the update event of the wrapped component and if it is emitted, the UPDATE_INPUT mutation is committed.

    connect({
      mutationsToEvents: {
        update: 'UPDATE_INPUT'
      }
    })

    lifecycle is lifecycle hooks for a Vue component. The lifecycle hooks receive a Vuex store for their first argument. You can dispatch some actions or mutations in the lifecycle hooks.

    connect returns another function. The function expects a component name and the component constructor. The component name should be a string. It is useful to specify the component in the debug phase.

    createConnect(fn) -> ConnectFunction

    Creates a customized connect function. fn is transform function of the wrapper component options and will receive a component options object and a lifecycle option of the connect function. You may want to inject some additional lifecycle hooks in fn.

    Example of defining vue-router lifecycle hooks:

    // connect.js
    import { createConnect } from 'vuex-connect'
    
    export const connect = createConnect((options, lifecycle) => {
      options.beforeRouteEnter = lifecycle.beforeRouteEnter
    
      options.beforeRouteUpdate = function(to, from, next) {
        return lifecycle.beforeRouteUpdate.call(this, this.store, to, from, next)
      }
    
      options.beforeRouteLeave = function(to, from, next) {
        return lifecycle.beforeRouteLeave.call(this, this.store, to, from, next)
      }
    })

    It can be used as:

    import { connect } from './connect'
    import { Example } from './example'
    
    export default connect({
      lifecycle: {
        beforeRouteEnter(to, from, next) {
          // ...
        },
    
        beforeRouteUpdate(store, to, from, next) {
          // ...
        },
    
        beforeRouteLeave(store, to, from, next) {
          // ...
        }
      }
    })('example', Example)

    License

    MIT

    Visit original content creator repository https://github.com/ktsn/vuex-connect
  • products-api

    Atelier Products API

    About

    The goal of this project was to build a scalable RESTful API service for the Products component of an e-commerce website. The service was optimized by leveraging database indexing, connection pooling, and Redis caching. The server and Postgres database were containerized with Docker and scaled using Docker Swarm.

    Development

    1. Clone this repo https://github.com/sdc-cirrus/products-api.git
    2. Install Docker
    3. Create a .env file in the project’s root directory and provide POSTGRES_USER, POSTGRES_PASSWORD, POSTGRES_DB, POSTGRES_HOST and POSTGRES_PORT.
    4. In the project’s root directory, run:
    docker-compose up
    

    Tech Stack

    ETL

    1. Inherited legacy dataset consisting of 40M records.
    2. Used Node streams to transform data.
    3. Designed appropriate schemas for the data
    4. Leveraged Node-PG connection pools and DBeaver to load the data into the local development database and deployed database.
    5. Incorporated Docker Swarm to replicate multiple instances of the server and scale the API

    Testing

    Used Loader.IO and Artillery to stress test the API.

    Routes

    HTTP METHOD Endpoint Returns Status
    GET /products/:productId/related Returns the IDs of products related to the specified product 200
    GET /products/:productId/styles Returns the all styles available for the given product 200
    GET /products/:productId Returns all product level information for a specified product id 200
    GET /products/list Retrieves the list of products 200


    Visit original content creator repository
    https://github.com/sdc-cirrus/products-api

  • airthings-webhook-receiver

    airthings-webhook-receiver

    Little application to receive data from Airthings send by the Webhook integration.

    Installation

    Service

    The service keeps your airthings-webhook-receiver up and running while recording data with systemd-journal.

    $ sudo -i
    $ cd /opt
    $ git checkout git@github.com:RindusIoTJam/airthings-webhook-receiver.git
    $ cd airthings-webhook-receiver/src
    $ npm install
    $ cp ../contrib/airthings-webhook-receiver.service /lib/systemd/system/airthings-webhook-receiver.service
    $ systemctl daemon-reload
    $ systemctl enable airthings-webhook-receiver.service
    $ systemctl start airthings-webhook-receiver.service && journalctl -fu airthings-webhook-receiver.service

    Airthings SSL endpoint

    Ensure the endpoint is secured by SSL with e.g. Apache adding the following to you SSL VirtualHost:

    ProxyPass        /airthings/ http://127.0.0.1:9883/
    ProxyPassReverse /airthings/ http://127.0.0.1:9883/

    STunnel could also be an option.

    Airthings Webhook

    Setup https://{fqdn-of-your-server}/airthings/hook as the webhook URL.

    Promtail

    Promtail reads the journal and stores data in Grafana/Loki. Setup

    scrape_configs:
    - job_name: journal
      journal:
        json: false
        max_age: 12h
        labels:
          job: systemd-journal
      pipeline_stages:
      - json:
          expressions:
            data: data[0]
      - json:
          expressions:
            serialNumber:
            measurementSystem:
          source: data
      - labels:
          serialNumber:
      - labels:
          measurementSystem:
      - output:
          source: data
      relabel_configs:
        - source_labels: ['__journal__systemd_unit']
          target_label: 'unit'

    Grafana/Loki

    avg_over_time(
      {job="systemd-journal",unit="airthings-webhook-receiver.service"}
      | json
      | unwrap temp
      | __error__="" [5m]
    ) by(serialNumber)
    

    Local Testing

    $ curl -XPOST http://127.0.0.1:9883/hook -d '{"key1":"value1"}' -H "Content-Type: application/json"

    Visit original content creator repository
    https://github.com/RindusIoTJam/airthings-webhook-receiver

  • conflict-juggler.nvim

    conflict-juggler.nvim

    This project took inspiration from the VSCode extension Conflict Squeezer.

    Over the years working with Git, I’ve had to resolve many merge conflicts. When I used VSCode, the Conflict Squeezer extension saved me a lot of time on the more challenging merge conflicts. After moving on to Neovim, I started missing this functionality so much that I decided to rewrite it. One late night later and a bit less hair on my head, here it is!

    Features

    This plugin creates a command called ConflictJuggler that:

    • Scans the current buffer and removes the conflict markers when both sides of the conflict are the same.

    • Simplifies the conflict when the start or the end are the same, moving the matching lines out of the block.

    • Supports nested conflict markers (WOW! 🤩)

    Installation

    You can install this plugin with the plugin manager of your choice. Here are a few examples:

    Lazy

    { 'pv-hernandez/conflict-juggler.nvim' }

    Plug

    Plug 'pv-hernandez/conflict-juggler.nvim'

    Usage

    To use this plugin you just need to run the command :ConflictJuggler on a buffer with conflict markers. The conflict block will get simplified.

    The conflict below:

    <<<<<<< HEAD
    aaa
    bbb
    =======
    bbb
    >>>>>>> remote
    

    Becomes:

    <<<<<<< HEAD
    aaa
    =======
    >>>>>>> remote
    bbb
    

    The strategy is to make both sides of the conflict match so that the lines get moved by the plugin. So say we want the final file to have the “aaa” line. We add that line to the “remote” part of the conflict:

    <<<<<<< HEAD
    aaa
    =======
    aaa
    >>>>>>> remote
    bbb
    

    And run the command again:

    aaa
    bbb
    

    This is a small example but it get really useful when the conflict blocks span tens or hundreds of lines, most of them are the same with indentation changes or some other small change that is hard to spot quickly.

    Real world example

    A Flutter project is an example of code base where this kind of conflict happens frequently, because of the amount of nested objects you create. In this example one change added a Theme around the widget tree, and another change added a Column around the widget tree. The entire tree got indented by a different amount in each change, so the conflict block spans the entire build method:

    Widget build(BuildContext context) {
    <<<<<<< HEAD
        return Theme(
            data: ...,
            child: A(
                child: B(
                    children: [
                        C(),
                        // Many items
                        C(),
                    ],
                ),
            ),
    |||||||
        return A(
            child: B(
                children: [
                    C(),
                    // Many items
                    C(),
                ],
            ),
    =======
        return Column(
            children: [
                A(
                    child: B(
                        children: [
                            C(),
                            // Many items
                            C(),
                        ],
                    ),
                ),
            ],
    >>>>>>> remote
        );
    }

    It is difficult to know if there are more changes in the middle of the widget tree by just looking at the conflict block, so we make the start and the end of the changes the same and run the command :ConflictJuggler:

    Widget build(BuildContext context) {
    <<<<<<< HEAD
        return Theme(
            data: ...,
            child: Column(
                children: [
                    A(
                        child: B(
                            children: [
                                C(),
                                // Many items
                                C(),
                            ],
                        ),
                    ),
                ],
            ),
    |||||||
        return A(
            child: B(
                children: [
                    C(),
                    // Many items
                    C(),
                ],
            ),
    =======
        return Theme(
            data: ...,
            child: Column(
                children: [
                    A(
                        child: B(
                            children: [
                                C(),
                                // Many items
                                C(),
                            ],
                        ),
                    ),
                ],
            ),
    >>>>>>> remote
        );
    }

    Widget build(BuildContext context) {
        return Theme(
            data: ...,
            child: Column(
                children: [
                    A(
                        child: B(
                            children: [
                                C(),
    <<<<<<< HEAD
                                // Many items
    |||||||
        return A(
            child: B(
                children: [
                    C(),
                    // Many items
                    C(),
                ],
            ),
    =======
                                // Many items
    >>>>>>> remote
                                C(),
                            ],
                        ),
                    ),
                ],
            ),
        );
    }

    The conflict makers move closer together, around one part of the widget tree that is different between both sides. Now you can keep making both sides match and running the command until the conflict is gone.

    This process avoids missing on small changes in the middle of the conflict block.

    If you like this plugin, don’t forget to share it with others!

    Visit original content creator repository
    https://github.com/pv-hernandez/conflict-juggler.nvim

  • conflict-juggler.nvim

    conflict-juggler.nvim

    This project took inspiration from the VSCode extension Conflict Squeezer.

    Over the years working with Git, I’ve had to resolve many merge conflicts. When I used VSCode, the Conflict Squeezer extension saved me a lot of time on the more challenging merge conflicts. After moving on to Neovim, I started missing this functionality so much that I decided to rewrite it. One late night later and a bit less hair on my head, here it is!

    Features

    This plugin creates a command called ConflictJuggler that:

    • Scans the current buffer and removes the conflict markers when both sides of the conflict are the same.

    • Simplifies the conflict when the start or the end are the same, moving the matching lines out of the block.

    • Supports nested conflict markers (WOW! 🤩)

    Installation

    You can install this plugin with the plugin manager of your choice. Here are a few examples:

    Lazy

    { 'pv-hernandez/conflict-juggler.nvim' }

    Plug

    Plug 'pv-hernandez/conflict-juggler.nvim'

    Usage

    To use this plugin you just need to run the command :ConflictJuggler on a buffer with conflict markers. The conflict block will get simplified.

    The conflict below:

    <<<<<<< HEAD
    aaa
    bbb
    =======
    bbb
    >>>>>>> remote
    

    Becomes:

    <<<<<<< HEAD
    aaa
    =======
    >>>>>>> remote
    bbb
    

    The strategy is to make both sides of the conflict match so that the lines get moved by the plugin. So say we want the final file to have the “aaa” line. We add that line to the “remote” part of the conflict:

    <<<<<<< HEAD
    aaa
    =======
    aaa
    >>>>>>> remote
    bbb
    

    And run the command again:

    aaa
    bbb
    

    This is a small example but it get really useful when the conflict blocks span tens or hundreds of lines, most of them are the same with indentation changes or some other small change that is hard to spot quickly.

    Real world example

    A Flutter project is an example of code base where this kind of conflict happens frequently, because of the amount of nested objects you create. In this example one change added a Theme around the widget tree, and another change added a Column around the widget tree. The entire tree got indented by a different amount in each change, so the conflict block spans the entire build method:

    Widget build(BuildContext context) {
    <<<<<<< HEAD
        return Theme(
            data: ...,
            child: A(
                child: B(
                    children: [
                        C(),
                        // Many items
                        C(),
                    ],
                ),
            ),
    |||||||
        return A(
            child: B(
                children: [
                    C(),
                    // Many items
                    C(),
                ],
            ),
    =======
        return Column(
            children: [
                A(
                    child: B(
                        children: [
                            C(),
                            // Many items
                            C(),
                        ],
                    ),
                ),
            ],
    >>>>>>> remote
        );
    }

    It is difficult to know if there are more changes in the middle of the widget tree by just looking at the conflict block, so we make the start and the end of the changes the same and run the command :ConflictJuggler:

    Widget build(BuildContext context) {
    <<<<<<< HEAD
        return Theme(
            data: ...,
            child: Column(
                children: [
                    A(
                        child: B(
                            children: [
                                C(),
                                // Many items
                                C(),
                            ],
                        ),
                    ),
                ],
            ),
    |||||||
        return A(
            child: B(
                children: [
                    C(),
                    // Many items
                    C(),
                ],
            ),
    =======
        return Theme(
            data: ...,
            child: Column(
                children: [
                    A(
                        child: B(
                            children: [
                                C(),
                                // Many items
                                C(),
                            ],
                        ),
                    ),
                ],
            ),
    >>>>>>> remote
        );
    }

    Widget build(BuildContext context) {
        return Theme(
            data: ...,
            child: Column(
                children: [
                    A(
                        child: B(
                            children: [
                                C(),
    <<<<<<< HEAD
                                // Many items
    |||||||
        return A(
            child: B(
                children: [
                    C(),
                    // Many items
                    C(),
                ],
            ),
    =======
                                // Many items
    >>>>>>> remote
                                C(),
                            ],
                        ),
                    ),
                ],
            ),
        );
    }

    The conflict makers move closer together, around one part of the widget tree that is different between both sides. Now you can keep making both sides match and running the command until the conflict is gone.

    This process avoids missing on small changes in the middle of the conflict block.

    If you like this plugin, don’t forget to share it with others!

    Visit original content creator repository
    https://github.com/pv-hernandez/conflict-juggler.nvim