diff --git a/Makefile b/Makefile index 4380f7b..82a2f9d 100644 --- a/Makefile +++ b/Makefile @@ -40,7 +40,7 @@ $(BUILD_DIR)/%.o: $(LIB_DIR)/%.cpp $(CXX) $(CXXFLAGS) $(INCLUDES) -c $< -o $@ -pie test: all - ./$(TARGET) + ./$(TARGET) test/journal.md -o output.html clean: rm -rf $(BUILD_DIR) $(TARGET) diff --git a/lib/inlineNode.cpp b/lib/inlineNode.cpp index e0cced9..c69a482 100644 --- a/lib/inlineNode.cpp +++ b/lib/inlineNode.cpp @@ -24,4 +24,7 @@ string BoldItalicNode::ToHtml() const { string CodeNode::ToHtml() const { return "" + this->content + ""; } -string RawTextNode::ToHtml() const { return this->content; }; +string LinkNode::ToHtml() const { + return "link + "\" target=\"_blank\">" + this->content + + ""; +} diff --git a/lib/inlineNode.h b/lib/inlineNode.h index 9866b6e..a412297 100644 --- a/lib/inlineNode.h +++ b/lib/inlineNode.h @@ -118,15 +118,20 @@ public: }; /** - * @desc A raw text node. + * @desc An anchor/link container node. * - * This node returns only it content, with no formatting at all. + * This node is used to create an inline anchor tag. This node returns the + * following {content}. * * @author Hayden Hargreaves (hhargreaves2006@gmail.com) */ -class RawTextNode : public InlineNode { +class LinkNode : public InlineNode { +protected: + std::string link; + public: - RawTextNode(std::string content) : InlineNode(content) {}; + LinkNode(std::string link, std::string content) + : InlineNode(content), link(link) {}; std::string ToHtml() const; }; diff --git a/lib/parser.cpp b/lib/parser.cpp index 32f5deb..a0cc6af 100644 --- a/lib/parser.cpp +++ b/lib/parser.cpp @@ -138,7 +138,7 @@ std::unique_ptr Parser::ParseHeading() { ConsumeWhiteSpace(); // This should call parse inline - auto text_nodes = ParseInline(); + auto text_nodes = ParseInlineHeading(); for (auto &text_node : text_nodes) { node->AddChild(std::move(text_node)); } @@ -158,12 +158,28 @@ std::unique_ptr Parser::ParseList(bool ordered) { Consume(ordered ? 2 : 1); ConsumeWhiteSpace(); + // std::unique_ptr Parser::ParseParagraph() { + // auto node = std::make_unique(); + // + // // This should call parse inline + // auto text_nodes = ParseInline(); + // for (auto &text_node : text_nodes) { + // node->AddChild(std::move(text_node)); + // } + // + // if (node->IsEmpty()) + // return nullptr; + // + // return node; + // } + // Parse until either '\n\n' (exit) or the next list element is found ('* ' // or '1.') If '\n\n', then create a node and exit - auto children = ParseInlineListContent(); - for (auto &child : children) { - node->AddChild(std::move(child)); - } + auto element = ParseInlineListContent(); + node->AddChild(std::move(element)); + // for (auto &child : children) { + // node->AddChild(std::move(child)); + // } char c = Peek(); char c_next = Peek(1); @@ -212,7 +228,7 @@ std::unique_ptr Parser::ParseCodeBlock() { Consume(); } - auto text_node = std::make_unique(str); + auto text_node = std::make_unique(str); node->AddChild(std::move(text_node)); return node; @@ -229,6 +245,14 @@ vector> Parser::ParseInline() { if (c == '\n' && Peek(1) == '\n') break; + if (c == '[') { + PushTextNode(nodes, str); + auto node = ParseLink(); + if (!node->IsEmpty()) + nodes.push_back(std::move(node)); + continue; + } + if (c == '*' && Peek(1) == '*' && Peek(2) == '*') { PushTextNode(nodes, str); auto node = ParseBoldItalic(); @@ -277,6 +301,14 @@ vector> Parser::ParseInlineHeading() { if (c == '\n') break; + if (c == '[') { + PushTextNode(nodes, str); + auto node = ParseLink(); + if (!node->IsEmpty()) + nodes.push_back(std::move(node)); + continue; + } + if (c == '*' && Peek(1) == '*' && Peek(2) == '*') { PushTextNode(nodes, str); auto node = ParseBoldItalic(); @@ -315,50 +347,70 @@ vector> Parser::ParseInlineHeading() { return nodes; } -vector> Parser::ParseInlineListContent() { - vector> nodes; +std::unique_ptr Parser::ParseInlineListContent() { + vector> children; string str; while (!IsEOF()) { char c = Peek(); - char c_next = Peek(1); + // char c_next = Peek(1); // If this char and next char are both newlines: then we have an empty line, // we should stop. if (c == '\n' && Peek(1) == '\n') break; - // Check if a list block has been found - if ((c == '*' || c == '-' || c == '+') && (c_next == ' ' || c_next == '\t')) - break; + // A single newline: We should consume whitespace and check if the next + // character is a list item and the following item is a space + if (c == '\n') { + PushTextNode(children, str); + ConsumeWhiteSpace(); + char new_c = Peek(); + char new_c_next = Peek(1); - if (std::isdigit(c) && c_next == '.') - break; + if ((new_c == '*' || new_c == '-' || new_c == '+') && + (new_c_next == ' ' || new_c_next == '\t')) + break; + + if (std::isdigit(new_c) && new_c_next == '.') + break; + + str += ' '; + continue; + } + + if (c == '[') { + PushTextNode(children, str); + auto node = ParseLink(); + if (!node->IsEmpty()) + children.push_back(std::move(node)); + continue; + } if (c == '*' && Peek(1) == '*' && Peek(2) == '*') { - PushTextNode(nodes, str); + PushTextNode(children, str); auto node = ParseBoldItalic(); if (!node->IsEmpty()) - nodes.push_back(std::move(node)); + children.push_back(std::move(node)); continue; } else if (c == '*' && Peek(1) == '*') { - PushTextNode(nodes, str); + PushTextNode(children, str); auto node = ParseBold(); if (!node->IsEmpty()) - nodes.push_back(std::move(node)); + children.push_back(std::move(node)); continue; } else if (c == '*') { - PushTextNode(nodes, str); + PushTextNode(children, str); auto node = ParseItalic(); if (!node->IsEmpty()) - nodes.push_back(std::move(node)); + children.push_back(std::move(node)); continue; } if (c == '`') { - PushTextNode(nodes, str); + PushTextNode(children, str); auto node = ParseCode(); if (!node->IsEmpty()) - nodes.push_back(std::move(node)); + children.push_back(std::move(node)); continue; } @@ -368,8 +420,15 @@ vector> Parser::ParseInlineListContent() { } // Push the last node, if the string is not empty - PushTextNode(nodes, str); - return nodes; + PushTextNode(children, str); + + // Create the list node with the children appended + auto element = std::make_unique(); + for (auto &child : children) { + element->AddChild(std::move(child)); + } + + return element; } std::unique_ptr Parser::ParseItalic() { @@ -460,6 +519,38 @@ std::unique_ptr Parser::ParseCode() { return std::make_unique(str); } +std::unique_ptr Parser::ParseLink() { + // Consume '[' + Consume(); + + string content; + while (!IsEOF()) { + char c = Peek(); + if (c == ']') + break; + + content += c; + Consume(); + } + + // Consume '](' + Consume(2); + + string link; + while (!IsEOF()) { + char c = Peek(); + if (c == ')') { + Consume(); + break; + } + + link += c; + Consume(); + } + + return std::make_unique(link, content); +} + void Parser::PushTextNode(vector> &nodes, string &str) { if (!str.empty()) nodes.push_back(std::move(std::make_unique(str))); diff --git a/lib/parser.h b/lib/parser.h index 12ef8b3..9298aa8 100644 --- a/lib/parser.h +++ b/lib/parser.h @@ -127,7 +127,7 @@ private: // The only differences are the exit condition vector> ParseInlineHeading(); - vector> ParseInlineListContent(); + std::unique_ptr ParseInlineListContent(); void PushTextNode(vector> &nodes, string &str); @@ -135,6 +135,7 @@ private: std::unique_ptr ParseBold(); std::unique_ptr ParseBoldItalic(); std::unique_ptr ParseCode(); + std::unique_ptr ParseLink(); char Peek(size_t offset = 0); void Consume(size_t count = 1); diff --git a/lib/structureNode.cpp b/lib/structureNode.cpp index 2c5740f..6e73409 100644 --- a/lib/structureNode.cpp +++ b/lib/structureNode.cpp @@ -61,13 +61,26 @@ string ListNode::ToHtml() const { ss << (this->ordered ? "
    " : "
      ") << "\n"; for (const auto &child : this->GetChilren()) { - ss << "
    • " << *child << "
    • " << "\n"; + ss << *child; } ss << (this->ordered ? "
" : "") << "\n"; return ss.str(); } +string ListElementNode::ToHtml() const { + std::stringstream ss; + ss << "
  • "; + + for (const auto &child : this->GetChilren()) { + ss << *child; + } + + ss << "
  • " << "\n"; + + return ss.str(); +} + string CodeBlockNode::ToHtml() const { std::stringstream ss; diff --git a/lib/structureNode.h b/lib/structureNode.h index 186268e..f803bef 100644 --- a/lib/structureNode.h +++ b/lib/structureNode.h @@ -111,6 +111,11 @@ public: std::string ToHtml() const; }; +class ListElementNode : public StructureNode { +public: + std::string ToHtml() const; +}; + /** * @desc A code block container node. * diff --git a/test/hello.html b/test/hello.html deleted file mode 100644 index b15db82..0000000 --- a/test/hello.html +++ /dev/null @@ -1,55 +0,0 @@ - - - - - - Document - - -

    MarkdownToHtmlTranspiler

    -

    Project Overview

    -

    The goal is to create a program that reads a file containing text formatted in a simple version of Markdown and converts it into a valid HTML file. The program will need to identify and translate specific syntax (e.g., # Heading to

    Heading

    , *text* to text).

    -

    Implementation Requirements (Generated by Gemini)

    -

    Class Hierarchy: Design a class hierarchy to represent the components of your Markdown document. An abstract base class, Element, can define common behavior. Derived classes would then represent specific types of elements, such as Heading, Paragraph, BoldText, and ListItem. This is a perfect example of inheritance and polymorphism.

    -

    Object Composition: A Document class can be composed of multiple Element objects, representing the entire file. A Parser class would be composed of helper methods to break down the input string and build the Document object. This shows how you can build a complex system from smaller, self-contained objects.

    -

    File I/O and Exceptions: You will need to use ifstream to read the Markdown file and ofstream to write the generated HTML file. Your code should use exceptions to gracefully handle potential errors, such as a file not being found.

    -

    Operator Overloading: Overload the << stream insertion operator for your Element and Document classes. This would allow you to easily print the generated HTML to the console or write it to a file, making your code cleaner and more readable.

    -

    UML Diagram: The complexity of the class relationships makes a UML diagram an essential part of the project. It will help you plan your design and will be a key component of your submission.

    -

    Recursive Descent Parser: This is the primary algorithm you'll use. It's a top-down parsing technique where a set of recursive functions "descend" through the grammar of your simple Markdown language. For example, a parse_document() function would call parse_line(), which in turn might call parse_bold_text() or parse_italic_text(). This method is intuitive and easy to implement for a simple grammar.

    -

    Stack: A stack is essential for handling nested elements. For instance, if you allow bold text inside italic text (_This is bold and italic text_), you can push the _ token onto the stack and then push the token. When you encounter the closing , you check if the top of the stack matches. This ensures that all tags are correctly opened and closed. Your presentation can visually demonstrate this process with a stack diagram.

    -

    Hash Map or Map: A hash map (std::unordered_map) or a map (std::map) can be used to efficiently store and retrieve the HTML equivalent for each Markdown tag. For example, you could map # to

    or * to . This provides O(1) average-case lookup time.

    -

    Contribution Policy

    -
    Branching When working on this project, please use a feature branch (i.e. feature/parser) with a descriptive name. feature/a is not a descriptive name. These branches should be branched off the most recent main branch, we will not make use of a dev or staging branch since the project is small in scale as well as time. However, if the project becomes larger or out-of-control, a dev/staging branch will be implemented.
    -
    Commits
    -

    When working, it is best practice to commit code as much as possible, without being over zealous. For example, when a feature or bug is complete, its time to commit. But when you have to make a new function, that does not mean its time. Each team member should use their best judgment.

    -

    Commit messages a little bit more important, when working in a team, it is important to provide strong, clear and concise commit messages. In this project, the team will use a simple formula:

    -

    (SUBJECT) Title: textual description

    -

    i.e. (FIX) Rendering completed: explain what changed in short.

    -
    Pushing
    -

    When working in a feature branch, pushing and pulling has no restrictions. Feel free to do as much (or as little) as possible. However, you CANNOT push directly to main, the VCS will not allow you to do so, but do not make that mistake. When you are ready to merge a feature, you will create a PR and once it has been reviewed and approved it will be automatically merged in.

    -
    Pull Requests (PR)
    -

    Once a feature is complete, you will create a pull request. Before a request can be merged into main, one approval is required (which cannot be the author). This practice is to promote team work and encourage code reviews. Each team member is expected to check in frequently and review as often as they are able to, however, there is no defined time requirement. Personal communication is totally acceptable as a means to request approval, since I am unsure if this platform will notify members.

    -
    Issues
    -

    If a bug, issue, or otherwise concern is noticed the first thing the team member should do is create an issue. An issue should be descriptive and contain everything another team member needs to understand the issue and its context. This way, a new team member can tackle the issue without contextual gaps.

    -

    If a member would like to work on the issue themself, the assignee field is where this should be defined. If a member would like help from another member, they should assign the other team member to the issue, and leave a comment in the issue itself describing what help is needed.

    -

    Labels are important for understanding what type of issues/bugs exist in the application. When a bug is created, make sure the proper labels are applied. These labels will be abstract, such as: bug, fix or feature and they will also be specific, such as: parser, i/o or processer. A combination of both styles of labels allows other team members to understand what is going on. If a member feels an issue is missing, they are free to create new ones, but there is a such thing as too many labels a few per issue is totally fine. They are not meant to replace the description.

    -

    Priority is the final important factor to consider. In this project, priority will be defined using labels as well. The policy defined above will apply here to priority labels as well. However, these labels are mutually exclusive.

    -
    Projects (Sprints)
    -

    The use of the projects tab in the VCS will allow the team to remain organized as create notes and action items that should be completed before one another. These resemble sprints from the AGILE development life cycle. A new "project" should be created when a large piece of functionality needs to be created. Issues can and should be attached to the projects they are related too. This will continue to encourage teamwork and organization.

    -

    Projects should have defined criteria, such as input and outputs, expectations and a semi-defined timeline. Once a description and is defined, tasks can be added and moved around as needed. The team will use Kanban project types, as they are simple and easy to understand for new team members. Reference [here](https://www.markdownguide.org/basic-syntax/)

    -

    Headings, h# tags

    -

    Header Level 1 ->

    Content

    ## Header Level 2 ->

    Content

    ### Header Level 3 ->

    Content

    #### Header Level 4 ->

    Content

    ##### Header Level 5 ->
    Content
    ###### Header Level 6 ->
    Content

    -

    Alternate syntax (n number of =/-)

    -

    Header Level 1 ->

    Content

    ================

    -

    Header Level 2 ->

    Content

    ----------------

    -

    Paragraph tags

    -

    Hello world ->

    Hello world

    -

    This is also a paragraph ->

    this is also a paragraph regardless

    regardless

    -

    However this is a break, because it ends with two spaces ->

    However
    this is a break, because it ends with two spaces

    -

    Double returns also

    -

    yields new paragraphs ->

    Double returns also

    yields new paragraphs

    -

    italic -> italic bold -> bold italic bold -> italic bold

    -

    hello world -> [TextClass: hello, BoldClass: world]

    - - - \ No newline at end of file diff --git a/test/input.html b/test/input.html deleted file mode 100644 index d5b4d49..0000000 --- a/test/input.html +++ /dev/null @@ -1,25 +0,0 @@ - - - - - - Document - - -

    Reference [here](https://www.markdownguide.org/basic-syntax/)

    -

    Headings, h# tags

    -

    Header Level 1 ->

    Content

    ## Header Level 2 ->

    Content

    ### Header Level 3 ->

    Content

    #### Header Level 4 ->

    Content

    ##### Header Level 5 ->
    Content
    ###### Header Level 6 ->
    Content
    -

    Alternate syntax (n number of =/-)

    -

    Header Level 1 ->

    Content

    ================

    -

    Header Level 2 ->

    Content

    ----------------

    -

    Paragraph tags

    -

    Hello world ->

    Hello world

    -

    This is also a paragraph ->

    this is also a paragraph regardless

    regardless

    -

    However this is a break, because it ends with two spaces ->

    However
    this is a break, because it ends with two spaces

    -

    Double returns also

    -

    yields new paragraphs ->

    Double returns also

    yields new paragraphs

    -

    italic -> italic bold -> bold italic bold -> italic bold

    -

    hello world -> [TextClass: hello, BoldClass: world]

    - - - \ No newline at end of file diff --git a/test/journal.md b/test/journal.md new file mode 100644 index 0000000..98bf247 --- /dev/null +++ b/test/journal.md @@ -0,0 +1,295 @@ +Date: 2025/02/25 +Desc: After using Neovim for years, I tried the JetBrains products for a month. Here's what I found. + +# 30 Days of JetBrains: My Vim Cleanse + +Jet Brains Logo + +###### Author: Hayden Hargreaves + +###### Published: 03/27/2025 + +## Background + +I have been using Neovim exclusively for over two years, and in those years I have +become *"blazingly fast"* and my developer experience has increased exponentially. +Inspired by popular Twitch Streamer, [The Primeagen](https://www.twitch.tv/theprimeagen), +I started using Neovim in late 2022. I started with just note-taking using a popular +Neovim distribution called [NVChad](https://nvchad.com), which allowed me to get a feel +for the tool and how it can be used. But it was too hard, the learned curve was too steep. + +Eventually, a few months later, I ran into a YouTube video from The Primeagen where he was +programming live on Twitch. While watching this video, I was in awe of his speed, efficiency, +and the tools he was using looked amazing. I decided to give Neovim another try, but this time +I didn't use NVChad. I wanted to learn how to configure the editor myself, because that is a +huge part of why Neovim is so popular, and why I still use it to this day. + +After countless hours of configuration and problems, I finally had a tool that I could call my +own and begin writing software with. At first, I struggled to understand the appeal. Vim motions +are confusing and hard to remember. But with time, I became fast, really fast. I started to get +comments from my peers in class asking how I type so fast and what editor I am using. Most people +have the same response to my response: "Eh, Vim? Isn't that old?" My answer, Neovim is new fork +of Vim which is being maintained by a large team of amazing open source developers. + +Over the years, I have tried to convince countless peers to "take the vim pill" and give it a +try. But after being rejected by almost everyone, finally realized that the tool each developer +uses really doesn't matter as long as they enjoy it and feel comfortable. However, I strongly +encourage everyone to give Vim a try at some point. You may love it! + +That final realization is the fuel for this experiment. + +## Why JetBrains? + +In my first semesters at Embry-Riddle, I had the pleasure of meeting many experienced professionals +who scoff at my choice of tooling. "To each their own," I say! But after the third or fourth time, +I started to think that maybe I am missing something? In my Neovim editor I have everything I could +ever need, countless language servers (LS or LSP) with autocomplete and other features, database +integration, AI tools like CoPilot, lighting fast navigation via +[Telescope](https://github.com/nvim-telescope/telescope.nvim) and +[Harpoon](https://github.com/ThePrimeagen/harpoon), syntax highlighting via +[TreeSitter](https://github.com/nvim-treesitter/nvim-treesitter), and even git integration from +[Fugitive](https://github.com/tpope/vim-fugitive). Needless to say, if I want something new, I can +get it. Granted, plugins are not exclusive to Neovim. Visual Studio Code (VSC), another popular +integrated development environment (IDE), also has a large plugin ecosystem. However, this argument +is to rebut against the frequent complaint that Neovim is lacking in features and cannot serve as a +modern IDE. + +That now begs the question, why JetBrains products? The answer is simple: they are the best. +There is hardly any competition in the proprietary development tools space, the two biggest competitors +being VSCode (as previously mentioned) and the JetBrains suite of tools. I have experience using both +tools. I spent lots of time using IntelliJ from JetBrains when I learned Java, and this experience +opened my eyes to the power of an IDE vs. a typical text editor (Neovim). However, Java is not a simple +language compared to GoLang (my primary language) or Python (what I use in many of my University classes). +For that reason, I avoided using such powerful tools when writing code in languages that I did not see +a huge benefit from. But I have always had a sweet spot for IntelliJ; It was how I was introduced to the +world of software development. For that reason, I decided to choose JetBrains for this experiment. + +## Why Change Now? + +Another great question! If I love Neovim so much and I am so productive with a tool, why try something +new? A professor who I have grown particularly fond of, has always poked fun at me for my choice of +tooling. He frequently mentions that I should try something else because when I get into the work force, +I will not be able to use Vim. I have finally had enough! Just kidding. He is right, if I am only +competent with a single tool, I will struggle in the future. So I decided to spend 30 days using only +JetBrains products. + +With my student email, I qualify for free access to the JetBrains suite, which is a huge factor in this +choice. A subscription for a JetBrains editor is nearly $100 a year, **per editor**. In this experiment, +I will be using **PyCharm**, **WebStorm**, **GoLand**, **CLion**, and **DataGrip**. I do not want to spend +hundreds of dollars on an editor when so many free options exist, but their education benefits, I cannot +use that as an excuse. Furthermore, I may as well take advantage of the benefits while I have them! + +## The Migration + +Switching from such a personal tool to a proprietary tool is a big jump. My biggest concern was the motions +I have become so accustomed too. Each JetBrains product has a plugin called **IdeaVim** which implements +vim motions natively into the editor. In the past, I have used the **VSCode Neovim** plugin, but it felt +slow, buggy and simply just bad. However, even in just the short time writing this article, I have not noticed +any large issues with the vim motions in the JetBrains plugin. + +Another thing I will miss during these 30 days is the plugins I use in my Neovim configuration. Below, you +can see a collection of each plugin I use in my setup. Quite a few! Some of my favorites being **Harpoon** +and **Telescope** which allow me to move between buffers (similar but different from files) with ease. During +this experiment, I will not install any of these plugins into my JetBrains editors. I could very easily convert +any of these editors into a copy of my Neovim setup, but that defeats the whole purpose of this trial! I will +use this editor with very few plugins to allow for more native feel, and to take full advantage of the features +provided by the tool without handicapping myself to my comfort zone. + +
    + +#### My Neovim Plugins + +- [dashboard-nvim](https://github.com/glepnir/dashboard-nvim) - A dashboard for Neovim. +- [emmet-vim](https://github.com/mattn/emmet-vim) - Emmet support for HTML, CSS, etc. +- [github-copilot.vim](https://github.com/github/copilot.vim) - GitHub Copilot integration. +- [Harpoon](https://github.com/ThePrimeagen/harpoon) - Quickly jump between files. +- [hex.nvim](https://github.com/folke/hex.nvim) - Provides hex editing capabilities. +- [lspkind.nvim](https://github.com/simrat39/lspkind.nvim) - Adds icons to LSP completions. +- [lualine.nvim](https://github.com/nvim-lualine/lualine.nvim) - A statusline plugin. +- [LuaSnip](https://github.com/L3MON4D3/LuaSnip) - A snippet engine. +- [markdown-preview.nvim](https://github.com/iamcco/markdown-preview.nvim) - Preview Markdown files. +- [Nixvim](https://github.com/nix-community/nixvim) - Integrates Neovim with the Nix package manager for reproducible + configurations. +- [noice.nvim](https://github.com/folke/noice.nvim) - Replaces Vim's default notification system. +- [none-ls.nvim](https://github.com/nvim-lua/none-ls.nvim) - A "null-ls" implementation for non-LSP servers. +- [nvim-cmp](https://github.com/hrsh7th/nvim-cmp) - A completion plugin. +- [nvim-colorizer.lua](https://github.com/norcalli/nvim-colorizer.lua) - Displays colors in the editor. +- [nvim-git signs](https://github.com/lewis6991/nvim-git-signs) - Displays Git changes in the sign column. +- [nvim-marks.lua](https://github.com/chentoast/marks.nvim) - Manages marks. +- [nvim-notify](https://github.com/rcarriga/nvim-notify) - Another notification plugin. +- [nvim-smart-splits](https://github.com/mrjones2014/nvim-smart-splits) - Manages window splits. +- [nvim-surround](https://github.com/tpope/nvim-surround) - Easily change surrounding characters (quotes, parentheses, + etc.). +- [nvim-treesitter-undo](https://github.com/RRRRRRRRRRRRRRRR/nvim-treesitter-undo) - Improves undo/redo with Treesitter. +- [nvim-trouble](https://github.com/folke/nvim-trouble.nvim) - Displays diagnostics in a more user-friendly way. +- [nvim-ufo](https://github.com/folke/nvim-ufo) - Improves code folding. +- [render-markdown.nvim](https://github.com/MeanderingProgrammer/render-markdown.nvim) - Another Markdown renderer. +- [rose-pine](https://github.com/rose-pine/neovim) - Rose pine color theme. +- [lspsaga.nvim](https://github.com/glepnir/lspsaga.nvim) - Enhances the LSP experience. +- [tailwind-tools.nvim](https://github.com/luckasRanarison/tailwind-tools.nvim) - Provides Tailwind CSS integration. +- [Telescope](https://github.com/nvim-telescope/telescope.nvim) - A highly extensible fuzzy finder. +- [vim-fugitive](https://github.com/tpope/vim-fugitive) - Git integration. +- [wakatime.vim](https://github.com/wakatime/vim-wakatime) - WakaTime integration for tracking your coding time. + +
    + +To remain some level of productivity, I did install a handful of select plugins in each of the +JetBrains IDEs. They are listed below: + +#### JetBrains Plugins + +- [IdeaVim](https://plugins.jetbrains.com/plugin/164-ideavim) - Vim motions. +- [GitHub](https://plugins.jetbrains.com/plugin/13115-github) - GitHub integration, installed by default. +- [GitLab](https://plugins.jetbrains.com/plugin/22857-gitlab) - GitLab integration, installed by default. +- [Grazie Pro](https://plugins.jetbrains.com/plugin/16136-grazie-pro) - Grammar help and completion. +- [Rose Pine](https://plugins.jetbrains.com/plugin/18141-ros-pine) - Rose pine color theme. +- [NixIdea](https://plugins.jetbrains.com/plugin/8607-nixidea) - Nix and NixOS tooling. +- [WakaTime](https://plugins.jetbrains.com/plugin/7425-wakatime) - WakaTime integration for tracking your coding time. + +## Artificial Intelligence + +With AI on the rise, I am faced with the question of using an AI tool in my editor or not. As you've seen above, +I use **GitHub Copilot** in my Neovim config, which has served me well. In my experience, it is bad at generating +complex code, but it does an exceptional job with helping me write comments and boilerplate code. However, I have +found that I think less and rely on Copilot far too much. During these 30 days, I hope to break this habit, which +I have heard called "the Copilot pause." When writing some code, I often wait a second for Copilot to write it for +me. This pause proves my reliance on AI has grown too far. + +> "After using copilots for a while, you’ve probably noticed the good ol’ copilot “pause.” You know, that moment when +> you’re just waiting for AI to write what you’re thinking so you can press tab and go on with your life. That’s the pause." +> ~[Eric Zakariasson](https://anyblockers.com/posts/avoid-the-copilot-pause) + +So, during this experiment, I will not use any AI integration in my editor. This includes the **Jetbrains AI Assistant**, +which is advertised ad nauseam in the IDEs. But I am no programming master, and I do still see benefit in using +LLMs to help with simple tasks and idea creation. So, I will continue to use chatbots like **ChatGPT** or Google's +**Gemini** to help me with day-to-day use cases. + +## Editor Setup +I have written this entire document in WebStorm, but up to now, its only been a few hours of the switch. But one thing I +have learned is that the IdeaVim plugin allows for configuration in a `~/.ideavimrc` file. Very similar to Vim, yay! In +this fill you can configure all kinds of things, however, I tried not to go overboard with the configuration file and tried +to configure most of the editor in the application settings. Again, to make the feeling less "vim-like" and to have a more +out-of-the-box experience. + +```vim +" These do not work very well :( +" However, there is a setting for this in the IDE +" 'Move Line Up/Down' in 'Keymap' settings +" +" vnoremap J :m '>+1gv=gv +" vnoremap K :m '<-2gv=gv + +" Indent lines with tab and shift-tab +nnoremap V> +nnoremap V< +vnoremap >gv +vnoremap