<?xml version="1.0" encoding="utf-8"?><?xml-stylesheet type="text/xsl" href="rss.xsl"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/">
    <channel>
        <title>Ammar Najjar's Blog</title>
        <link>https://ammar-najjar.com/blog</link>
        <description>Articles on software development, AI tools, and book reviews by Ammar Najjar — Engineering Team Lead and Software Architect.</description>
        <lastBuildDate>Sun, 14 Jun 2026 12:00:00 GMT</lastBuildDate>
        <docs>https://validator.w3.org/feed/docs/rss2.html</docs>
        <generator>https://github.com/jpmonette/feed</generator>
        <language>en</language>
        <copyright>Copyright © 2026 Ammar Najjar</copyright>
        <item>
            <title><![CDATA[Local AI on a MacBook Pro with 16 GB RAM: What Actually Works]]></title>
            <link>https://ammar-najjar.com/blog/local-ai-setup</link>
            <guid>https://ammar-najjar.com/blog/local-ai-setup</guid>
            <pubDate>Sun, 14 Jun 2026 12:00:00 GMT</pubDate>
            <description><![CDATA[How to run AI coding assistants locally on a 16 GB MacBook Pro M1 — what models fit in memory, which tools actually work, and what to skip.]]></description>
            <content:encoded><![CDATA[<p>Running AI coding assistants locally sounds appealing — no API costs, your code stays on your machine, and you get a Claude Code-like experience for free. But getting it to actually work well on a 16 GB MacBook Pro M1 takes more trial and error than most guides admit.</p>
<p>This post summarizes months of real-world testing across multiple tools, runtimes, and models. The conclusion surprised me.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-goal">The Goal<a href="https://ammar-najjar.com/blog/local-ai-setup#the-goal" class="hash-link" aria-label="Direct link to The Goal" title="Direct link to The Goal" translate="no">​</a></h2>
<p>Build a local, subscription-free AI development environment that can support:</p>
<ul>
<li class="">Architecture discussions and documentation</li>
<li class="">Repository exploration and planning</li>
<li class="">Code modifications, refactoring, and bug fixes</li>
<li class="">ADRs and Obsidian notes</li>
<li class="">Angular, NestJS, .NET, Docker, and Kubernetes projects</li>
</ul>
<p>The setup should run entirely locally on a MacBook Pro with 16 GB RAM.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-biggest-discovery">The Biggest Discovery<a href="https://ammar-najjar.com/blog/local-ai-setup#the-biggest-discovery" class="hash-link" aria-label="Direct link to The Biggest Discovery" title="Direct link to The Biggest Discovery" translate="no">​</a></h2>
<p>Before getting into tool comparisons and benchmarks, the most important finding was this:</p>
<blockquote>
<p><strong>Agent capability &gt; Coding benchmark score</strong></p>
</blockquote>
<p>A model that can find files, edit code, use tools, and execute multi-step workflows is often more useful than a model with a higher coding benchmark score that cannot reliably do those things.</p>
<p>This single insight changed which models and tools came out on top.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tools-evaluated">Tools Evaluated<a href="https://ammar-najjar.com/blog/local-ai-setup#tools-evaluated" class="hash-link" aria-label="Direct link to Tools Evaluated" title="Direct link to Tools Evaluated" translate="no">​</a></h2>
<p>Three coding agent frameworks were tested in depth:</p>
<table><thead><tr><th>Tool</th><th>What It Is</th></tr></thead><tbody><tr><td><strong>OpenCode</strong></td><td>Agent-oriented terminal interface</td></tr><tr><td><strong>Aider</strong></td><td>Git-focused code editing assistant</td></tr><tr><td><strong>Continue.dev</strong></td><td>VSCode extension with chat and autocomplete</td></tr></tbody></table>
<p>Two local model runtimes were compared:</p>
<table><thead><tr><th>Runtime</th><th>What It Is</th></tr></thead><tbody><tr><td><strong>Ollama</strong></td><td>Simple, popular CLI-based local model server</td></tr><tr><td><strong>LM Studio</strong></td><td>GUI-based model manager with an OpenAI-compatible API</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tool-comparison-results">Tool Comparison Results<a href="https://ammar-najjar.com/blog/local-ai-setup#tool-comparison-results" class="hash-link" aria-label="Direct link to Tool Comparison Results" title="Direct link to Tool Comparison Results" translate="no">​</a></h2>
<table><thead><tr><th>Tool</th><th style="text-align:center">Architecture</th><th style="text-align:center">Docs</th><th style="text-align:center">Coding</th><th style="text-align:center">Tool Use</th><th style="text-align:center">Ease of Use</th><th style="text-align:center">Verdict</th></tr></thead><tbody><tr><td><strong>OpenCode</strong></td><td style="text-align:center">10/10</td><td style="text-align:center">10/10</td><td style="text-align:center">8/10</td><td style="text-align:center">9/10</td><td style="text-align:center">8/10</td><td style="text-align:center"><strong>Primary Workspace</strong></td></tr><tr><td>Continue.dev</td><td style="text-align:center">6/10</td><td style="text-align:center">4/10</td><td style="text-align:center">7/10</td><td style="text-align:center">5/10</td><td style="text-align:center">10/10</td><td style="text-align:center">IDE Companion</td></tr><tr><td>Aider</td><td style="text-align:center">2/10</td><td style="text-align:center">1/10</td><td style="text-align:center">5/10</td><td style="text-align:center">3/10</td><td style="text-align:center">6/10</td><td style="text-align:center">Not recommended</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="opencode">OpenCode<a href="https://ammar-najjar.com/blog/local-ai-setup#opencode" class="hash-link" aria-label="Direct link to OpenCode" title="Direct link to OpenCode" translate="no">​</a></h3>
<p>OpenCode won as the primary workspace because it is built for agentic workflows. It plans, explores repositories, generates documents, and writes long-form content reliably.</p>
<p><strong>Strengths:</strong> Planning, architecture analysis, documentation, repository exploration, long-form content<br>
<strong>Weaknesses:</strong> Requires a larger context window; performance depends heavily on model tool-use capability</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="aider-the-surprise-disappointment">Aider: The Surprise Disappointment<a href="https://ammar-najjar.com/blog/local-ai-setup#aider-the-surprise-disappointment" class="hash-link" aria-label="Direct link to Aider: The Surprise Disappointment" title="Direct link to Aider: The Surprise Disappointment" translate="no">​</a></h3>
<p>Aider has a good reputation for code editing, and that reputation may be deserved in other setups. In this workflow it underperformed significantly.</p>
<p>During testing, a simple prompt like <code>Create a file containing one line: hello</code> caused Aider to generate unrelated JavaScript instead of creating the file. It also failed at file discovery and documentation generation.</p>
<p>In this workflow it was <strong>not recommended</strong>.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="continuedev">Continue.dev<a href="https://ammar-najjar.com/blog/local-ai-setup#continuedev" class="hash-link" aria-label="Direct link to Continue.dev" title="Direct link to Continue.dev" translate="no">​</a></h3>
<p>The simplest of the three. Install the VSCode extension, point it at a local model, and it works. Great for IDE chat, quick explanations, and autocomplete. Not the right tool for architecture work or anything requiring multi-step reasoning.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="runtime-comparison">Runtime Comparison<a href="https://ammar-najjar.com/blog/local-ai-setup#runtime-comparison" class="hash-link" aria-label="Direct link to Runtime Comparison" title="Direct link to Runtime Comparison" translate="no">​</a></h2>
<p>The runtime turned out to matter more than expected.</p>
<table><thead><tr><th>Runtime</th><th style="text-align:center">OpenCode Experience</th><th style="text-align:center">Model Management</th><th style="text-align:center">Troubleshooting</th><th style="text-align:center">Verdict</th></tr></thead><tbody><tr><td><strong>LM Studio</strong></td><td style="text-align:center">10/10</td><td style="text-align:center">10/10</td><td style="text-align:center">10/10</td><td style="text-align:center"><strong>Preferred for OpenCode</strong></td></tr><tr><td>Ollama</td><td style="text-align:center">6/10</td><td style="text-align:center">7/10</td><td style="text-align:center">6/10</td><td style="text-align:center">Secondary (Continue.dev)</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="what-went-wrong-with-ollama-and-opencode">What went wrong with Ollama and OpenCode<a href="https://ammar-najjar.com/blog/local-ai-setup#what-went-wrong-with-ollama-and-opencode" class="hash-link" aria-label="Direct link to What went wrong with Ollama and OpenCode" title="Direct link to What went wrong with Ollama and OpenCode" translate="no">​</a></h3>
<p>When connecting OpenCode to local models through Ollama, several problems appeared:</p>
<ul>
<li class="">Some models returned no answer at all</li>
<li class="">Some responses were truncated mid-output</li>
<li class="">Context handling was opaque — hard to know what was actually loaded</li>
<li class="">OpenCode's agent prompt was too heavy for small contexts</li>
<li class="">The experience was inconsistent across sessions</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="why-lm-studio-worked-better">Why LM Studio worked better<a href="https://ammar-najjar.com/blog/local-ai-setup#why-lm-studio-worked-better" class="hash-link" aria-label="Direct link to Why LM Studio worked better" title="Direct link to Why LM Studio worked better" translate="no">​</a></h3>
<p>After switching OpenCode to LM Studio, the problems mostly disappeared:</p>
<ul>
<li class="">The exact loaded model ID was visible via <code>/v1/models</code></li>
<li class="">Context length was visible and configurable in the UI</li>
<li class="">The OpenAI-compatible API made troubleshooting straightforward</li>
<li class="">Models that failed through Ollama worked correctly through LM Studio</li>
</ul>
<p><strong>Key observation:</strong> The models themselves were not the main problem. The Ollama + OpenCode combination was less reliable on this machine. LM Studio + OpenCode behaved better with the same models.</p>
<p>You can verify what LM Studio is serving at any time:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl http://localhost:1234/v1/models</span><br></div></code></pre></div></div>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="model-comparison">Model Comparison<a href="https://ammar-najjar.com/blog/local-ai-setup#model-comparison" class="hash-link" aria-label="Direct link to Model Comparison" title="Direct link to Model Comparison" translate="no">​</a></h2>
<table><thead><tr><th>Model</th><th style="text-align:center">Speed</th><th style="text-align:center">Tool Use</th><th style="text-align:center">Architecture</th><th style="text-align:center">Docs</th><th style="text-align:center">Coding</th><th style="text-align:center">Final Role</th></tr></thead><tbody><tr><td><strong>qwen3.5:9b</strong></td><td style="text-align:center">7/10</td><td style="text-align:center">10/10</td><td style="text-align:center">10/10</td><td style="text-align:center">9/10</td><td style="text-align:center">8/10</td><td style="text-align:center"><strong>Main model</strong></td></tr><tr><td><strong>qwen3.5:2b</strong></td><td style="text-align:center">10/10</td><td style="text-align:center">9/10</td><td style="text-align:center">6/10</td><td style="text-align:center">7/10</td><td style="text-align:center">5/10</td><td style="text-align:center"><strong>Fast agent</strong></td></tr><tr><td>Ministral 3B</td><td style="text-align:center">8/10</td><td style="text-align:center">8/10</td><td style="text-align:center">7/10</td><td style="text-align:center">8/10</td><td style="text-align:center">6/10</td><td style="text-align:center">Alternative</td></tr><tr><td>qwen2.5-coder:7b</td><td style="text-align:center">6/10</td><td style="text-align:center">4/10</td><td style="text-align:center">6/10</td><td style="text-align:center">5/10</td><td style="text-align:center">8/10</td><td style="text-align:center">Coding specialist (weak agent)</td></tr><tr><td>llama3.1:8b</td><td style="text-align:center">5/10</td><td style="text-align:center">4/10</td><td style="text-align:center">6/10</td><td style="text-align:center">6/10</td><td style="text-align:center">6/10</td><td style="text-align:center">Rejected</td></tr><tr><td>gemma4</td><td style="text-align:center">5/10</td><td style="text-align:center">2/10</td><td style="text-align:center">6/10</td><td style="text-align:center">5/10</td><td style="text-align:center">7/10</td><td style="text-align:center">Rejected</td></tr></tbody></table>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="real-world-tool-use-test-results">Real-world tool use test results<a href="https://ammar-najjar.com/blog/local-ai-setup#real-world-tool-use-test-results" class="hash-link" aria-label="Direct link to Real-world tool use test results" title="Direct link to Real-world tool use test results" translate="no">​</a></h3>
<p>One of the most revealing tests was simply asking each model to create and edit files using OpenCode's tools:</p>
<table><thead><tr><th>Model</th><th style="text-align:center">Creates Files</th><th style="text-align:center">Edits Files</th><th style="text-align:center">Uses Tools</th></tr></thead><tbody><tr><td>qwen3.5:9b</td><td style="text-align:center">✅</td><td style="text-align:center">✅</td><td style="text-align:center">✅</td></tr><tr><td>qwen3.5:2b</td><td style="text-align:center">✅</td><td style="text-align:center">✅</td><td style="text-align:center">✅</td></tr><tr><td>Ministral 3B</td><td style="text-align:center">✅</td><td style="text-align:center">Sometimes</td><td style="text-align:center">✅</td></tr><tr><td>qwen2.5-coder:7b</td><td style="text-align:center">Mixed</td><td style="text-align:center">Mixed</td><td style="text-align:center">Mixed</td></tr><tr><td>gemma4</td><td style="text-align:center">❌</td><td style="text-align:center">❌</td><td style="text-align:center">❌</td></tr></tbody></table>
<p>gemma4 refused editing tasks despite seeing the available tools. llama3.1:8b worked but delivered poor value for its resource cost.</p>
<p><code>qwen2.5-coder:7b</code> scores well on coding benchmarks but has <strong>mixed tool use</strong> in practice — it underperformed as an OpenCode agent compared to expectations. This is the core finding: benchmark scores do not predict agent capability.</p>
<p><strong>qwen3.5:9b</strong> is the main model: strong reasoning, reliable tool use, excellent for architecture and documentation work. <strong>qwen3.5:2b</strong> is the fast companion for quick tasks and notes.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-context-window-problem">The Context Window Problem<a href="https://ammar-najjar.com/blog/local-ai-setup#the-context-window-problem" class="hash-link" aria-label="Direct link to The Context Window Problem" title="Direct link to The Context Window Problem" translate="no">​</a></h2>
<p>This was one of the most important — and most surprising — findings.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="opencode-needs-more-context-than-you-expect">OpenCode needs more context than you expect<a href="https://ammar-najjar.com/blog/local-ai-setup#opencode-needs-more-context-than-you-expect" class="hash-link" aria-label="Direct link to OpenCode needs more context than you expect" title="Direct link to OpenCode needs more context than you expect" translate="no">​</a></h3>
<p>When OpenCode was connected to a model with 8k context, this error appeared immediately:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">n_keep: 26460 &gt;= n_ctx: 8192</span><br></div></code></pre></div></div>
<p>Then later, after attempts to reduce it:</p>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">n_keep: 8232 &gt;= n_ctx: 8192</span><br></div></code></pre></div></div>
<p>OpenCode's initial system prompt alone exceeds 8k tokens. This means <strong>the model never even received the user's first message</strong> — the context was already full before the conversation started.</p>
<p>The fix: set LM Studio context to <strong>32,768 tokens</strong> and match that in the OpenCode model configuration.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="recommended-context-sizes">Recommended context sizes<a href="https://ammar-najjar.com/blog/local-ai-setup#recommended-context-sizes" class="hash-link" aria-label="Direct link to Recommended context sizes" title="Direct link to Recommended context sizes" translate="no">​</a></h3>
<table><thead><tr><th>Tool</th><th>Runtime</th><th style="text-align:right">Context</th></tr></thead><tbody><tr><td>OpenCode</td><td>LM Studio</td><td style="text-align:right">32,768</td></tr><tr><td>Continue.dev</td><td>Either</td><td style="text-align:right">8,192</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-final-stack">The Final Stack<a href="https://ammar-najjar.com/blog/local-ai-setup#the-final-stack" class="hash-link" aria-label="Direct link to The Final Stack" title="Direct link to The Final Stack" translate="no">​</a></h2>
<p>After all this testing, the recommended setup has three modes:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="primary-architecture--documentation-work">Primary: Architecture &amp; Documentation Work<a href="https://ammar-najjar.com/blog/local-ai-setup#primary-architecture--documentation-work" class="hash-link" aria-label="Direct link to Primary: Architecture &amp; Documentation Work" title="Direct link to Primary: Architecture &amp; Documentation Work" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">OpenCode + LM Studio + qwen3.5:9b</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Context: 32,768</span><br></div></code></pre></div></div>
<p>Use for: architecture reviews, documentation, ADRs, planning, repository exploration, Obsidian notes</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="fast-mode-quick-tasks">Fast Mode: Quick Tasks<a href="https://ammar-najjar.com/blog/local-ai-setup#fast-mode-quick-tasks" class="hash-link" aria-label="Direct link to Fast Mode: Quick Tasks" title="Direct link to Fast Mode: Quick Tasks" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">OpenCode + LM Studio + qwen3.5:2b</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Context: 32,768</span><br></div></code></pre></div></div>
<p>Use for: quick questions, notes, summaries, repository exploration</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ide-companion">IDE Companion<a href="https://ammar-najjar.com/blog/local-ai-setup#ide-companion" class="hash-link" aria-label="Direct link to IDE Companion" title="Direct link to IDE Companion" translate="no">​</a></h3>
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Continue.dev + LM Studio or Ollama + qwen3.5:2b</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Context: 8,192</span><br></div></code></pre></div></div>
<p>Use for: IDE chat, code explanations, autocomplete, small edits</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="quick-decision-table">Quick Decision Table<a href="https://ammar-najjar.com/blog/local-ai-setup#quick-decision-table" class="hash-link" aria-label="Direct link to Quick Decision Table" title="Direct link to Quick Decision Table" translate="no">​</a></h2>
<table><thead><tr><th>Task</th><th>Tool</th><th>Runtime</th><th>Model</th></tr></thead><tbody><tr><td>Architecture discussion</td><td>OpenCode</td><td>LM Studio</td><td>qwen3.5:9b</td></tr><tr><td>Writing documentation</td><td>OpenCode</td><td>LM Studio</td><td>qwen3.5:9b</td></tr><tr><td>Obsidian notes / ADRs</td><td>OpenCode</td><td>LM Studio</td><td>qwen3.5:9b</td></tr><tr><td>Repository exploration</td><td>OpenCode</td><td>LM Studio</td><td>qwen3.5:9b</td></tr><tr><td>Planning</td><td>OpenCode</td><td>LM Studio</td><td>qwen3.5:9b</td></tr><tr><td>Quick questions / notes</td><td>OpenCode</td><td>LM Studio</td><td>qwen3.5:2b</td></tr><tr><td>IDE chat / explanation</td><td>Continue.dev</td><td>LM Studio or Ollama</td><td>qwen3.5:2b</td></tr><tr><td>Autocomplete</td><td>Continue.dev</td><td>LM Studio or Ollama</td><td>qwen3.5:2b</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="installation">Installation<a href="https://ammar-najjar.com/blog/local-ai-setup#installation" class="hash-link" aria-label="Direct link to Installation" title="Direct link to Installation" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="ollama-for-continuedev">Ollama (for Continue.dev)<a href="https://ammar-najjar.com/blog/local-ai-setup#ollama-for-continuedev" class="hash-link" aria-label="Direct link to Ollama (for Continue.dev)" title="Direct link to Ollama (for Continue.dev)" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -fsSL https://ollama.com/install.sh | sh</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">ollama pull qwen3.5:2b</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="lm-studio-for-opencode">LM Studio (for OpenCode)<a href="https://ammar-najjar.com/blog/local-ai-setup#lm-studio-for-opencode" class="hash-link" aria-label="Direct link to LM Studio (for OpenCode)" title="Direct link to LM Studio (for OpenCode)" translate="no">​</a></h3>
<p>Download from the official LM Studio website. Load <code>qwen3.5:9b</code> for primary work or <code>qwen3.5:2b</code> for fast mode, set context to <strong>32,768</strong>, and start the local server.</p>
<blockquote>
<p><strong>Tip:</strong> LM Studio's <strong>Zen models</strong> are particularly effective on machines with limited RAM. They are well-optimized for Apple Silicon and worth trying if you want good performance without pushing memory limits.</p>
</blockquote>
<p>Verify the server is running:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl http://localhost:1234/v1/models</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="opencode-1">OpenCode<a href="https://ammar-najjar.com/blog/local-ai-setup#opencode-1" class="hash-link" aria-label="Direct link to OpenCode" title="Direct link to OpenCode" translate="no">​</a></h3>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">curl -fsSL https://opencode.ai/install | bash</span><br></div></code></pre></div></div>
<p>Configure <code>~/.config/opencode/opencode.json</code> to point at LM Studio. The model name in the config must match the exact <code>id</code> returned by <code>/v1/models</code>:</p>
<div class="language-json codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-json codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"$schema"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"https://opencode.ai/config.json"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token property" style="color:#36acaa">"provider"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token property" style="color:#36acaa">"lmstudio"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"npm"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"@ai-sdk/openai-compatible"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"LM Studio Local"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"options"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"baseURL"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"http://localhost:1234/v1"</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token property" style="color:#36acaa">"models"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"qwen/qwen3.5-9b"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Qwen 3.5 9B (Main)"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"limit"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">32768</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"output"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2048</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token property" style="color:#36acaa">"qwen/qwen3.5-2b"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"name"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"Qwen 3.5 2B (Fast)"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token property" style="color:#36acaa">"limit"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"context"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">32768</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token property" style="color:#36acaa">"output"</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2048</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">          </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="continuedev-1">Continue.dev<a href="https://ammar-najjar.com/blog/local-ai-setup#continuedev-1" class="hash-link" aria-label="Direct link to Continue.dev" title="Direct link to Continue.dev" translate="no">​</a></h3>
<p>Install the Continue extension in VSCode and point it at LM Studio or Ollama with <code>qwen3.5:2b</code>.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="lessons-learned">Lessons Learned<a href="https://ammar-najjar.com/blog/local-ai-setup#lessons-learned" class="hash-link" aria-label="Direct link to Lessons Learned" title="Direct link to Lessons Learned" translate="no">​</a></h2>
<table><thead><tr><th>Assumption</th><th>Reality</th></tr></thead><tbody><tr><td>Best coding benchmark = best agent</td><td>False — tool use capability matters more</td></tr><tr><td>Bigger model = better experience</td><td>False on 16 GB RAM</td></tr><tr><td>qwen2.5-coder:7b is the best local model</td><td>Strong coder, weak agent — mixed tool use</td></tr><tr><td>Aider is the best local coding tool</td><td>Not recommended for this workflow</td></tr><tr><td>Ollama is the best runtime</td><td>Not for OpenCode on this machine</td></tr><tr><td>Context size is a minor detail</td><td>False — OpenCode requires 32k minimum</td></tr><tr><td>LM Studio is just a GUI</td><td>False — it is the better OpenCode runtime</td></tr></tbody></table>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="future-upgrade-path">Future Upgrade Path<a href="https://ammar-najjar.com/blog/local-ai-setup#future-upgrade-path" class="hash-link" aria-label="Direct link to Future Upgrade Path" title="Direct link to Future Upgrade Path" translate="no">​</a></h2>
<p>If upgrading to <strong>32 GB RAM</strong>: evaluate larger models and bigger contexts.</p>
<p>If upgrading to <strong>64 GB RAM</strong>: re-evaluate 32B-class models and 64k contexts.</p>
<p>On 16 GB RAM, avoid running memory-heavy apps (Docker Desktop, multiple browser windows, IDE indexing) at the same time as a loaded 9B model.</p>
<hr>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://ammar-najjar.com/blog/local-ai-setup#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>The biggest discovery was not a model — it was a combination:</p>
<p><strong>OpenCode + LM Studio</strong> delivered a substantially better experience than any other setup tested.</p>
<p>The setup that works on a 16 GB MacBook Pro M1:</p>
<ul>
<li class=""><strong>OpenCode</strong> for thinking, planning, and writing</li>
<li class=""><strong>Continue.dev</strong> for IDE support and autocomplete</li>
<li class=""><strong>LM Studio</strong> as the OpenCode runtime</li>
<li class=""><strong>32k context</strong> for OpenCode, 8k for Continue.dev</li>
<li class=""><strong>qwen3.5:9b</strong> as the main model, <strong>qwen3.5:2b</strong> for fast tasks</li>
</ul>
<p>The principle that guided every decision: agent capability matters more than benchmark scores.</p>
<a href="https://ammar-najjar.com/blog/local-ai-setup#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Productivity</category>
            <category>Tools</category>
            <category>macOS</category>
            <category>ai</category>
        </item>
        <item>
            <title><![CDATA[Essential Development Tools: My Brewfile Setup]]></title>
            <link>https://ammar-najjar.com/blog/essential-dev-tools</link>
            <guid>https://ammar-najjar.com/blog/essential-dev-tools</guid>
            <pubDate>Mon, 23 Feb 2026 10:00:00 GMT</pubDate>
            <description><![CDATA[A software engineer's curated Brewfile — the macOS development tools worth installing, from terminal utilities to editors, explained with daily-use context.]]></description>
            <content:encoded><![CDATA[<p>As a software engineer, our toolset shapes how we work and what we can accomplish. Over time, I've curated a collection of essential tools that boost my productivity and make development more enjoyable. Here's a breakdown of my Homebrew setup, featuring the tools I use daily.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="tools-at-a-glance">Tools at a Glance<a href="https://ammar-najjar.com/blog/essential-dev-tools#tools-at-a-glance" class="hash-link" aria-label="Direct link to Tools at a Glance" title="Direct link to Tools at a Glance" translate="no">​</a></h2>
<table><thead><tr><th>Tool</th><th>What It's Good For</th></tr></thead><tbody><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#claude-code" class="">Claude Code</a></td><td>AI-powered coding assistant CLI</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#awscli" class="">awscli</a></td><td>AWS services management from the command line</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#colima" class="">colima</a></td><td>Lightweight Docker Desktop alternative for macOS</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#neovim" class="">neovim</a></td><td>Modern, extensible vim-based text editor</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#fd" class="">fd</a></td><td>Fast and user-friendly alternative to <code>find</code></td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#fzf" class="">fzf</a></td><td>Interactive fuzzy search for files, commands, and more</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#gh" class="">gh</a></td><td>GitHub CLI for managing repos, PRs, and issues</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#git-delta" class="">git-delta</a></td><td>Syntax-highlighted git diffs with side-by-side view</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#jq" class="">jq</a></td><td>Command-line JSON processor for parsing and manipulation</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#helm" class="">helm</a></td><td>Kubernetes package manager for deploying applications</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#k9s" class="">k9s</a></td><td>Terminal UI for managing Kubernetes clusters</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#kubernetes-cli" class="">kubernetes-cli</a></td><td>kubectl - Kubernetes cluster management tool</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#mactex" class="">MacTeX</a></td><td>Complete TeX distribution for macOS</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#obsidian" class="">Obsidian</a></td><td>Knowledge base and note-taking with markdown</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#maccy" class="">Maccy</a></td><td>Lightweight clipboard manager for macOS</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#uv" class="">uv</a></td><td>Fast Python package installer and resolver</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#ripgrep" class="">ripgrep</a></td><td>Ultra-fast recursive search tool</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#atuin" class="">atuin</a></td><td>Smart shell history search with sync across machines</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#tmux" class="">tmux</a></td><td>Terminal multiplexer for managing multiple sessions</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#warp" class="">Warp</a></td><td>Modern terminal with AI features and collaborative tools</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#git" class="">git</a></td><td>Distributed version control system</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#mise" class="">mise</a></td><td>Polyglot runtime version manager (replaces asdf, nvm, etc.)</td></tr><tr><td><a href="https://ammar-najjar.com/blog/essential-dev-tools#graphviz" class="">graphviz</a></td><td>Graph and diagram generation from text descriptions</td></tr></tbody></table>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="detailed-tool-breakdown">Detailed Tool Breakdown<a href="https://ammar-najjar.com/blog/essential-dev-tools#detailed-tool-breakdown" class="hash-link" aria-label="Direct link to Detailed Tool Breakdown" title="Direct link to Detailed Tool Breakdown" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="command-line-essentials">Command Line Essentials<a href="https://ammar-najjar.com/blog/essential-dev-tools#command-line-essentials" class="hash-link" aria-label="Direct link to Command Line Essentials" title="Direct link to Command Line Essentials" translate="no">​</a></h3>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="atuin">atuin<a href="https://ammar-najjar.com/blog/essential-dev-tools#atuin" class="hash-link" aria-label="Direct link to atuin" title="Direct link to atuin" translate="no">​</a></h4>
<p><a href="https://atuin.sh/" target="_blank" rel="noopener noreferrer" class="">Atuin</a> revolutionizes shell history management. Instead of the basic up-arrow history search, Atuin provides a searchable, syncable database of your shell commands across all your machines. It captures context like the directory you were in and exit codes, making it easy to find that command you ran three weeks ago.</p>
<p><strong>Key Features:</strong></p>
<ul>
<li class="">Full-text search through your entire shell history</li>
<li class="">Sync history across multiple machines (end-to-end encrypted)</li>
<li class="">Context-aware search (filter by directory, exit status, time)</li>
<li class="">Import existing shell history from bash, zsh, fish</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install atuin</span><br></div></code></pre></div></div>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="fd">fd<a href="https://ammar-najjar.com/blog/essential-dev-tools#fd" class="hash-link" aria-label="Direct link to fd" title="Direct link to fd" translate="no">​</a></h4>
<p><a href="https://github.com/sharkdp/fd" target="_blank" rel="noopener noreferrer" class="">fd</a> is a modern replacement for the classic <code>find</code> command. It's faster, more intuitive, and uses sensible defaults like ignoring <code>.gitignore</code> patterns automatically.</p>
<p><strong>Why I use it:</strong></p>
<ul>
<li class="">Simpler syntax than <code>find</code>: <code>fd pattern</code> vs <code>find . -name '*pattern*'</code></li>
<li class="">Colored output for better readability</li>
<li class="">Respects <code>.gitignore</code> by default</li>
<li class="">Blazingly fast parallel directory traversal</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install fd</span><br></div></code></pre></div></div>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="fzf">fzf<a href="https://ammar-najjar.com/blog/essential-dev-tools#fzf" class="hash-link" aria-label="Direct link to fzf" title="Direct link to fzf" translate="no">​</a></h4>
<p><a href="https://github.com/junegunn/fzf" target="_blank" rel="noopener noreferrer" class="">fzf</a> is an interactive fuzzy finder that integrates beautifully with the shell, vim, and other tools. It transforms how you search for files, navigate directories, and search command history.</p>
<p><strong>Common use cases:</strong></p>
<ul>
<li class=""><code>Ctrl+R</code> for fuzzy command history search</li>
<li class=""><code>Ctrl+T</code> for fuzzy file search</li>
<li class="">Integration with vim/neovim for file navigation</li>
<li class="">Custom scripts for searching anything (git branches, processes, etc.)</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install fzf</span><br></div></code></pre></div></div>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="ripgrep">ripgrep<a href="https://ammar-najjar.com/blog/essential-dev-tools#ripgrep" class="hash-link" aria-label="Direct link to ripgrep" title="Direct link to ripgrep" translate="no">​</a></h4>
<p><a href="https://github.com/BurntSushi/ripgrep" target="_blank" rel="noopener noreferrer" class="">ripgrep</a> (<code>rg</code>) is hands-down the fastest way to search through code. It automatically respects <code>.gitignore</code>, searches recursively, and provides colorized output with context.</p>
<p><strong>Performance highlights:</strong></p>
<ul>
<li class="">Faster than <code>grep</code>, <code>ag</code>, <code>ack</code>, and others</li>
<li class="">Respects <code>.gitignore</code> and hidden files by default</li>
<li class="">Supports multiple search patterns and file type filtering</li>
<li class="">Essential for large codebases</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install ripgrep</span><br></div></code></pre></div></div>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="tmux">tmux<a href="https://ammar-najjar.com/blog/essential-dev-tools#tmux" class="hash-link" aria-label="Direct link to tmux" title="Direct link to tmux" translate="no">​</a></h4>
<p><a href="https://github.com/tmux/tmux/wiki" target="_blank" rel="noopener noreferrer" class="">tmux</a> is a terminal multiplexer that lets you manage multiple terminal sessions from a single window. It's invaluable for remote work, long-running processes, and organizing your workspace.</p>
<p><strong>Why it's essential:</strong></p>
<ul>
<li class="">Persistent sessions that survive disconnections</li>
<li class="">Split panes and windows for multitasking</li>
<li class="">Session management for different projects</li>
<li class="">Scriptable and highly customizable</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install tmux</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="version-control--git-tools">Version Control &amp; Git Tools<a href="https://ammar-najjar.com/blog/essential-dev-tools#version-control--git-tools" class="hash-link" aria-label="Direct link to Version Control &amp; Git Tools" title="Direct link to Version Control &amp; Git Tools" translate="no">​</a></h3>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="git">git<a href="https://ammar-najjar.com/blog/essential-dev-tools#git" class="hash-link" aria-label="Direct link to git" title="Direct link to git" translate="no">​</a></h4>
<p><a href="https://git-scm.com/" target="_blank" rel="noopener noreferrer" class="">Git</a> is the backbone of modern software development. The distributed version control system that powers collaboration across teams and projects worldwide.</p>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install git</span><br></div></code></pre></div></div>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="git-delta">git-delta<a href="https://ammar-najjar.com/blog/essential-dev-tools#git-delta" class="hash-link" aria-label="Direct link to git-delta" title="Direct link to git-delta" translate="no">​</a></h4>
<p><a href="https://dandavison.github.io/delta" target="_blank" rel="noopener noreferrer" class="">Delta</a> transforms git diffs into beautiful, syntax-highlighted output with side-by-side comparisons. It makes code review and understanding changes much easier.</p>
<p><strong>Visual improvements:</strong></p>
<ul>
<li class="">Syntax highlighting in diffs</li>
<li class="">Side-by-side diff view</li>
<li class="">Line numbering and git blame integration</li>
<li class="">Configurable themes and styles</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install git-delta</span><br></div></code></pre></div></div>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="gh">gh<a href="https://ammar-najjar.com/blog/essential-dev-tools#gh" class="hash-link" aria-label="Direct link to gh" title="Direct link to gh" translate="no">​</a></h4>
<p><a href="https://cli.github.com/" target="_blank" rel="noopener noreferrer" class="">GitHub CLI</a> brings GitHub functionality to your terminal. Create PRs, review issues, manage repos, and automate workflows without leaving the command line.</p>
<p><strong>Favorite features:</strong></p>
<ul>
<li class=""><code>gh pr create</code> - Create pull requests from the terminal</li>
<li class=""><code>gh pr checkout</code> - Check out PRs by number</li>
<li class=""><code>gh repo clone</code> - Quickly clone repos</li>
<li class=""><code>gh run view</code> - Monitor GitHub Actions workflows</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install gh</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="container--kubernetes-tools">Container &amp; Kubernetes Tools<a href="https://ammar-najjar.com/blog/essential-dev-tools#container--kubernetes-tools" class="hash-link" aria-label="Direct link to Container &amp; Kubernetes Tools" title="Direct link to Container &amp; Kubernetes Tools" translate="no">​</a></h3>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="colima">colima<a href="https://ammar-najjar.com/blog/essential-dev-tools#colima" class="hash-link" aria-label="Direct link to colima" title="Direct link to colima" translate="no">​</a></h4>
<p><a href="https://github.com/abiosoft/colima" target="_blank" rel="noopener noreferrer" class="">Colima</a> is a lightweight container runtime for macOS that provides Docker and Kubernetes support with minimal resource usage. It's an excellent free alternative to Docker Desktop.</p>
<p><strong>Benefits:</strong></p>
<ul>
<li class="">Low memory footprint compared to Docker Desktop</li>
<li class="">Simple CLI interface: <code>colima start</code>, <code>colima stop</code></li>
<li class="">Supports both Docker and Kubernetes</li>
<li class="">Configurable CPU, memory, and disk allocation</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install colima</span><br></div></code></pre></div></div>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="kubernetes-cli">kubernetes-cli<a href="https://ammar-najjar.com/blog/essential-dev-tools#kubernetes-cli" class="hash-link" aria-label="Direct link to kubernetes-cli" title="Direct link to kubernetes-cli" translate="no">​</a></h4>
<p><a href="https://kubernetes.io/docs/reference/kubectl" target="_blank" rel="noopener noreferrer" class="">kubectl</a> is the official Kubernetes command-line tool for deploying applications, inspecting resources, and managing cluster operations.</p>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install kubernetes-cli</span><br></div></code></pre></div></div>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="helm">helm<a href="https://ammar-najjar.com/blog/essential-dev-tools#helm" class="hash-link" aria-label="Direct link to helm" title="Direct link to helm" translate="no">​</a></h4>
<p><a href="https://helm.sh/" target="_blank" rel="noopener noreferrer" class="">Helm</a> is the package manager for Kubernetes, making it easy to define, install, and upgrade complex Kubernetes applications using charts.</p>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install helm</span><br></div></code></pre></div></div>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="k9s">k9s<a href="https://ammar-najjar.com/blog/essential-dev-tools#k9s" class="hash-link" aria-label="Direct link to k9s" title="Direct link to k9s" translate="no">​</a></h4>
<p><a href="https://k9scli.io/" target="_blank" rel="noopener noreferrer" class="">k9s</a> provides a terminal UI for Kubernetes that makes cluster management visual and interactive. It's like having a dashboard in your terminal.</p>
<p><strong>Productivity boost:</strong></p>
<ul>
<li class="">Real-time cluster monitoring</li>
<li class="">Navigate pods, services, deployments with keyboard shortcuts</li>
<li class="">View logs, describe resources, port-forward with ease</li>
<li class="">Vim-like keybindings for efficiency</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install derailed/k9s/k9s</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="development-tools">Development Tools<a href="https://ammar-najjar.com/blog/essential-dev-tools#development-tools" class="hash-link" aria-label="Direct link to Development Tools" title="Direct link to Development Tools" translate="no">​</a></h3>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="mise">mise<a href="https://ammar-najjar.com/blog/essential-dev-tools#mise" class="hash-link" aria-label="Direct link to mise" title="Direct link to mise" translate="no">​</a></h4>
<p><a href="https://mise.jdx.dev/" target="_blank" rel="noopener noreferrer" class="">mise</a> (formerly rtx) is a polyglot runtime manager that handles multiple language versions seamlessly. It replaces tools like nvm, rbenv, pyenv, and asdf with a single, fast tool written in Rust.</p>
<p><strong>Language support:</strong></p>
<ul>
<li class="">Node.js, Python, Ruby, Go, Java, and more</li>
<li class="">Per-project version configuration with <code>.mise.toml</code></li>
<li class="">Faster than asdf (written in Rust)</li>
<li class="">Compatible with asdf plugins</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install mise</span><br></div></code></pre></div></div>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="uv">uv<a href="https://ammar-najjar.com/blog/essential-dev-tools#uv" class="hash-link" aria-label="Direct link to uv" title="Direct link to uv" translate="no">​</a></h4>
<p><a href="https://docs.astral.sh/uv" target="_blank" rel="noopener noreferrer" class="">uv</a> is a blazingly fast Python package installer and resolver written in Rust. It's 10-100x faster than pip and provides a better dependency resolution experience.</p>
<p><strong>Speed improvements:</strong></p>
<ul>
<li class="">Parallel downloads and installs</li>
<li class="">Global cache for packages</li>
<li class="">Better dependency conflict resolution</li>
<li class="">Drop-in replacement for pip commands</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install uv</span><br></div></code></pre></div></div>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="neovim">neovim<a href="https://ammar-najjar.com/blog/essential-dev-tools#neovim" class="hash-link" aria-label="Direct link to neovim" title="Direct link to neovim" translate="no">​</a></h4>
<p><a href="https://neovim.io/" target="_blank" rel="noopener noreferrer" class="">Neovim</a> is a modern, extensible fork of Vim with better defaults, Lua scripting support, and built-in LSP (Language Server Protocol) support. It's my primary editor for all development work.</p>
<p><strong>Modern features:</strong></p>
<ul>
<li class="">Native LSP support for IDE-like features</li>
<li class="">Lua configuration (more powerful than VimScript)</li>
<li class="">Treesitter for better syntax highlighting</li>
<li class="">Extensive plugin ecosystem</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install neovim</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="data--visualization-tools">Data &amp; Visualization Tools<a href="https://ammar-najjar.com/blog/essential-dev-tools#data--visualization-tools" class="hash-link" aria-label="Direct link to Data &amp; Visualization Tools" title="Direct link to Data &amp; Visualization Tools" translate="no">​</a></h3>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="jq">jq<a href="https://ammar-najjar.com/blog/essential-dev-tools#jq" class="hash-link" aria-label="Direct link to jq" title="Direct link to jq" translate="no">​</a></h4>
<p><a href="https://jqlang.github.io/jq" target="_blank" rel="noopener noreferrer" class="">jq</a> is a lightweight command-line JSON processor. It's indispensable for working with APIs, parsing JSON logs, and transforming data.</p>
<p><strong>Common uses:</strong></p>
<ul>
<li class="">Parse API responses: <code>curl api.example.com | jq '.data'</code></li>
<li class="">Filter arrays: <code>jq '.[] | select(.status == "active")'</code></li>
<li class="">Transform JSON structure</li>
<li class="">Pretty-print JSON</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install jq</span><br></div></code></pre></div></div>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="graphviz">graphviz<a href="https://ammar-najjar.com/blog/essential-dev-tools#graphviz" class="hash-link" aria-label="Direct link to graphviz" title="Direct link to graphviz" translate="no">​</a></h4>
<p><a href="https://graphviz.org/" target="_blank" rel="noopener noreferrer" class="">Graphviz</a> generates graphs and diagrams from text descriptions using the DOT language. It's perfect for visualizing system architecture, data structures, and relationships.</p>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install graphviz</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="aws-tools">AWS Tools<a href="https://ammar-najjar.com/blog/essential-dev-tools#aws-tools" class="hash-link" aria-label="Direct link to AWS Tools" title="Direct link to AWS Tools" translate="no">​</a></h3>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="awscli">awscli<a href="https://ammar-najjar.com/blog/essential-dev-tools#awscli" class="hash-link" aria-label="Direct link to awscli" title="Direct link to awscli" translate="no">​</a></h4>
<p><a href="https://aws.amazon.com/cli" target="_blank" rel="noopener noreferrer" class="">AWS CLI</a> is the official command-line interface for Amazon Web Services. It provides direct access to AWS services and is essential for automation and infrastructure management.</p>
<p><strong>Key operations:</strong></p>
<ul>
<li class="">Manage EC2 instances, S3 buckets, Lambda functions</li>
<li class="">Automate deployments and infrastructure changes</li>
<li class="">Query and filter resources with JMESPath</li>
<li class="">Profile management for multiple AWS accounts</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install awscli</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="desktop-applications">Desktop Applications<a href="https://ammar-najjar.com/blog/essential-dev-tools#desktop-applications" class="hash-link" aria-label="Direct link to Desktop Applications" title="Direct link to Desktop Applications" translate="no">​</a></h3>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="claude-code">Claude Code<a href="https://ammar-najjar.com/blog/essential-dev-tools#claude-code" class="hash-link" aria-label="Direct link to Claude Code" title="Direct link to Claude Code" translate="no">​</a></h4>
<p><a href="https://www.anthropic.com/claude/code" target="_blank" rel="noopener noreferrer" class="">Claude Code</a> is an AI-powered coding assistant that runs in your terminal. It helps with code generation, refactoring, debugging, and understanding complex codebases.</p>
<p><strong>Capabilities:</strong></p>
<ul>
<li class="">Intelligent code generation and completion</li>
<li class="">Context-aware suggestions</li>
<li class="">Code explanation and documentation</li>
<li class="">Debugging assistance</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install --cask claude-code</span><br></div></code></pre></div></div>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="warp">Warp<a href="https://ammar-najjar.com/blog/essential-dev-tools#warp" class="hash-link" aria-label="Direct link to Warp" title="Direct link to Warp" translate="no">​</a></h4>
<p><a href="https://www.warp.dev/" target="_blank" rel="noopener noreferrer" class="">Warp</a> is a modern, Rust-based terminal that reimagines the terminal experience with AI features, collaborative tools, and a more intuitive interface.</p>
<p><strong>Standout features:</strong></p>
<ul>
<li class="">AI command suggestions</li>
<li class="">Block-based output for easy copying</li>
<li class="">Command palette and searchable history</li>
<li class="">Collaborative terminal sharing</li>
<li class="">Built-in workflows and command templates</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install --cask warp</span><br></div></code></pre></div></div>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="maccy">Maccy<a href="https://ammar-najjar.com/blog/essential-dev-tools#maccy" class="hash-link" aria-label="Direct link to Maccy" title="Direct link to Maccy" translate="no">​</a></h4>
<p><a href="https://maccy.app/" target="_blank" rel="noopener noreferrer" class="">Maccy</a> is a lightweight, open-source clipboard manager for macOS. It keeps a history of everything you copy, making it easy to access previous clipboard contents.</p>
<p><strong>Simple but powerful:</strong></p>
<ul>
<li class="">Keyboard-driven interface</li>
<li class="">Search clipboard history</li>
<li class="">Pin frequently used items</li>
<li class="">Privacy-focused (no cloud sync)</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install --cask maccy</span><br></div></code></pre></div></div>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="obsidian">Obsidian<a href="https://ammar-najjar.com/blog/essential-dev-tools#obsidian" class="hash-link" aria-label="Direct link to Obsidian" title="Direct link to Obsidian" translate="no">​</a></h4>
<p><a href="https://obsidian.md/" target="_blank" rel="noopener noreferrer" class="">Obsidian</a> is a powerful knowledge base and note-taking application that works with local Markdown files. It's excellent for personal knowledge management and linking ideas together.</p>
<p><strong>Why I love it:</strong></p>
<ul>
<li class="">Local-first (your data stays on your machine)</li>
<li class="">Graph view for visualizing connections</li>
<li class="">Extensive plugin ecosystem</li>
<li class="">Markdown-based for portability</li>
<li class="">Backlinking and bi-directional links</li>
</ul>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install --cask obsidian</span><br></div></code></pre></div></div>
<hr>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="mactex">MacTeX<a href="https://ammar-najjar.com/blog/essential-dev-tools#mactex" class="hash-link" aria-label="Direct link to MacTeX" title="Direct link to MacTeX" translate="no">​</a></h4>
<p><a href="https://www.tug.org/mactex" target="_blank" rel="noopener noreferrer" class="">MacTeX</a> is a complete TeX distribution for macOS, essential for creating professional documents, academic papers, and technical documentation with LaTeX.</p>
<p><strong>Installation:</strong></p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install --cask mactex</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="managing-this-setup">Managing This Setup<a href="https://ammar-najjar.com/blog/essential-dev-tools#managing-this-setup" class="hash-link" aria-label="Direct link to Managing This Setup" title="Direct link to Managing This Setup" translate="no">​</a></h2>
<p>To replicate this setup on a new machine, I use Homebrew Bundle. You can download my <a href="https://raw.githubusercontent.com/ammarnajjar/ammarnajjar.github.io/main/blog/2026-02-23-essential-dev-tools/Brewfile" target="_blank" rel="noopener noreferrer" class="">Brewfile</a> and install all tools at once:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain"># Install Homebrew (if not already installed)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">/bin/bash -c "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/HEAD/install.sh)"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># Download the Brewfile and install all tools</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">curl -O https://raw.githubusercontent.com/ammarnajjar/ammarnajjar.github.io/main/blog/2026-02-23-essential-dev-tools/Brewfile</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">brew bundle install --file=Brewfile</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># Or to create a Brewfile from your current setup</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">brew bundle dump --file=Brewfile</span><br></div></code></pre></div></div>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://ammar-najjar.com/blog/essential-dev-tools#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>These tools form the foundation of my development environment. Each one solves a specific problem and integrates well with the others. The key is finding tools that match your workflow and actually using them consistently.</p>
<p>What tools are essential in your setup? I'm always interested in discovering new productivity boosters and improvements to existing workflows.</p>
<a href="https://ammar-najjar.com/blog/essential-dev-tools#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Productivity</category>
            <category>Tools</category>
            <category>Homebrew</category>
            <category>macOS</category>
        </item>
        <item>
            <title><![CDATA[Portfolio — a lightweight personal portfolio tracker]]></title>
            <link>https://ammar-najjar.com/blog/portfolio</link>
            <guid>https://ammar-najjar.com/blog/portfolio</guid>
            <pubDate>Fri, 12 Dec 2025 13:10:00 GMT</pubDate>
            <description><![CDATA[Design and implementation of a browser-first investment portfolio tracker built with React, TypeScript, and Vite — state handling, caching, and chart interaction explained.]]></description>
            <content:encoded><![CDATA[<p>This post explains the design and implementation of a small browser-first <a href="https://ammarnajjar.github.io/portfolio" target="_blank" rel="noopener noreferrer" class="">portfolio tracker</a> I built. It focuses on requirements, the challenges I ran into, and how the final solution addresses them, including code snippets that illustrate the state handling, caching, and chart interaction.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="goals-and-requirements">Goals and requirements<a href="https://ammar-najjar.com/blog/portfolio#goals-and-requirements" class="hash-link" aria-label="Direct link to Goals and requirements" title="Direct link to Goals and requirements" translate="no">​</a></h2>
<p>I collected these goals before coding:</p>
<ul>
<li class="">Privacy-first: keep user data local by default; no server-side storage unless the user opts in.</li>
<li class="">Low network usage: avoid redundant requests; be friendly to metered or slow connections.</li>
<li class="">Responsive UI: range selections and interactions should feel immediate, even when network calls are needed in the background.</li>
<li class="">Robustness: accept imported or hand-edited JSON and avoid re-fetching data unnecessarily.</li>
<li class="">Testable: core behaviors must have unit tests to prevent regressions.</li>
</ul>
<a href="https://ammar-najjar.com/blog/portfolio#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="implementation-challenges">Implementation challenges<a href="https://ammar-najjar.com/blog/portfolio#implementation-challenges" class="hash-link" aria-label="Direct link to Implementation challenges" title="Direct link to Implementation challenges" translate="no">​</a></h2>
<p>Several practical issues emerged while building the app:</p>
<ol>
<li class="">
<p>Range caching vs. freshness</p>
<ul>
<li class="">
<p>Problem: caching prevents redundant requests, but users expect recent short-term data to be fresh (e.g. 1 month view where today's price matters).</p>
</li>
<li class="">
<p>Trade-offs explored:</p>
<ul>
<li class="">Always fetch 1M (fresh) but cache longer ranges — simpler for correctness, but noisy for users who import histories.</li>
<li class="">Never fetch on range change (UI-only) — efficient but risks displaying stale data.</li>
</ul>
</li>
<li class="">
<p>Solution chosen: cache all ranges explicitly, but store <code>1M</code> as a persisted fetched-range so imported data that already covers 1M won't cause a fetch; additionally keep a history-based check (earliest candle <code>&lt;=</code> cutoff) to treat imported history as covering a range.</p>
</li>
</ul>
</li>
<li class="">
<p>Hand-edited or unsorted history arrays</p>
<ul>
<li class="">
<p>Problem: imported JSON may contain history arrays that are not sorted or that start later than the cutoff; naive checks that look at <code>history[0]</code> can be wrong.</p>
</li>
<li class="">
<p>Solution: compute the earliest timestamp across a history array (min) and compare normalized UTC dates to decide coverage.</p>
</li>
</ul>
</li>
<li class="">
<p>Per-item state persistence and migrations</p>
<ul>
<li class="">
<p>Problem: changing what is persisted (e.g., switching from implicit 1M to explicit <code>fetchedRanges</code>) requires handling existing localStorage states.</p>
</li>
<li class="">
<p>Solution: make persisted metadata explicit (write <code>1M</code> into <code>fetchedRanges</code> when appropriate) and optionally provide a migration/backfill path that populates <code>fetchedRanges</code> for existing users based on current <code>history</code>.</p>
</li>
</ul>
</li>
</ol>
<a href="https://ammar-najjar.com/blog/portfolio#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="add-stock-form">Add-stock form<a href="https://ammar-najjar.com/blog/portfolio#add-stock-form" class="hash-link" aria-label="Direct link to Add-stock form" title="Direct link to Add-stock form" translate="no">​</a></h2>
<p>The Add-stock form is a compact flow that lets users add a new symbol to their portfolio. It validates symbols client-side, shows recent quotes for confirmation, and writes the new item into the store. Newly added symbols are picked up by the auto-refresh loop after the first cycle.</p>
<p><img decoding="async" loading="lazy" alt="Add stock dialog" src="https://ammar-najjar.com/assets/images/add-a298c27d43ad6f778113a9025e159d60.png" width="3392" height="272" class="img_ev3q"></p>
<p><em><em>The Add-stock Form with symbol validation</em></em></p>
<a href="https://ammar-najjar.com/blog/portfolio#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-state-is-organized">How state is organized<a href="https://ammar-najjar.com/blog/portfolio#how-state-is-organized" class="hash-link" aria-label="Direct link to How state is organized" title="Direct link to How state is organized" translate="no">​</a></h2>
<ul>
<li class="">The app stores the entire application state under a single root key in <code>localStorage</code> (via a small helper) to keep reads/writes simple and make export/import predictable.</li>
<li class="">The store (<code>StoreProvider</code>) keeps the <code>portfolio</code> array in React state and writes it to <code>localStorage</code> on every change. Each <code>PortfolioItem</code> has fields like:</li>
</ul>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">type</span><span class="token plain"> </span><span class="token class-name">PortfolioItem</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	id</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token builtin">symbol</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	qty</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">number</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	avgPrice</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">number</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	currentPrice</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">number</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	history</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> time</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> value</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">number</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	fetchedRanges</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Range</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">// e.g. ['1D','1W','1M']</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	lastUpdated</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>Key helpers:</p>
<ol>
<li class=""><code>storage</code> helper (simple JSON wrapper around <code>localStorage</code>)</li>
</ol>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">readState</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token keyword" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">parse</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">localStorage</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getItem</span><span class="token punctuation" style="color:#393A34">(</span><span class="token constant" style="color:#36acaa">ROOT_KEY</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'{}'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">_</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">set</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">key</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> value</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">unknown</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> s </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">readState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">key</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> value</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> localStorage</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">setItem</span><span class="token punctuation" style="color:#393A34">(</span><span class="token constant" style="color:#36acaa">ROOT_KEY</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">JSON</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">stringify</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">s</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">get</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">key</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">string</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> defaultValue</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">any</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> s </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">readState</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">key</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">undefined</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> defaultValue </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> s</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">key</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<ol start="2">
<li class=""><code>StoreProvider</code> persistence and fetched-range writes</li>
</ol>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// persist portfolio whenever it changes</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">useEffect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> storage</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'portfolio'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> portfolio</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">portfolio</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// on successful fetch for an item:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">setPortfolio</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">current </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> current</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">id </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> item</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">id </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token operator" style="color:#393A34">...</span><span class="token plain">p</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	history</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">mergeHistories</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">history</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> history</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	lastUpdated</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Date</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">toISOString</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	fetchedRanges</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">Array</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">from</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token operator" style="color:#393A34">...</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fetchedRanges </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> fetchRange</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> p</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><br></div></code></pre></div></div>
<p><code>mergeHistories</code> is a small utility that merges two arrays of dated candles, preserving order and avoiding duplicates.</p>
<a href="https://ammar-najjar.com/blog/portfolio#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="chart-interaction-and-fetch-policy">Chart interaction and fetch policy<a href="https://ammar-najjar.com/blog/portfolio#chart-interaction-and-fetch-policy" class="hash-link" aria-label="Direct link to Chart interaction and fetch policy" title="Direct link to Chart interaction and fetch policy" translate="no">​</a></h2>
<p>When a user clicks a range button (e.g. <code>1D</code>, <code>1W</code>, <code>1M</code>), the UI should update immediately and decide whether a network refresh is needed. The decision algorithm in <code>handleRangeChange</code> is:</p>
<p><img decoding="async" loading="lazy" alt="Interactive portfolio chart" src="https://ammar-najjar.com/assets/images/chart-f04a5bbb524def73709918a68902b402.png" width="3392" height="1090" class="img_ev3q"></p>
<p><em><em>Interactive chart showing selected range and per-stock breakdown; this view updates when ranges change.</em></em></p>
<ol>
<li class="">Update local <code>range</code> state and call <code>setSelectedRange</code> in the store so the global selection reflects the UI.</li>
<li class="">If the store's <code>refreshPortfolioRange</code> is available, check whether any <code>PortfolioItem</code> already has the <code>targetRange</code> recorded in <code>fetchedRanges</code>.</li>
<li class="">For <code>1M</code> additionally check whether the item's <code>history</code> earliest date is at-or-before the 1M cutoff (UTC-normalized); if yes treat it as covered.</li>
<li class="">If none of the items cover the range, call <code>refreshPortfolioRange(targetRange)</code> which will fetch missing history and update <code>fetchedRanges</code>.</li>
</ol>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">handleRangeChange</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">targetRange</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Range</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token function" style="color:#d73a49">setRange</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">targetRange</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> setSelectedRange</span><span class="token operator" style="color:#393A34">?.</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">targetRange</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">refreshPortfolioRange</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> anyHave </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> portfolio</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">some</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">Array</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">isArray</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fetchedRanges</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fetchedRanges</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">includes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">targetRange</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">targetRange </span><span class="token operator" style="color:#393A34">===</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'1M'</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> </span><span class="token builtin">Array</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">isArray</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">history</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">history</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">length </span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">0</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">			</span><span class="token comment" style="color:#999988;font-style:italic">// compute earliest timestamp robustly</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">			</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> earliestTs </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Math</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">min</span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">...</span><span class="token plain">p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">history</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">h </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> Date</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">parse</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">h</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">time </span><span class="token operator" style="color:#393A34">+</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'T00:00:00'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">			</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> cutoff </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getRangeCutoff</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'1M'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">			</span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Date</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">UTC</span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Date</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">earliestTs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getFullYear</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Date</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">earliestTs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getMonth</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Date</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">earliestTs</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getDate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">				</span><span class="token operator" style="color:#393A34">&lt;=</span><span class="token plain"> Date</span><span class="token punctuation" style="color:#393A34">.</span><span class="token constant" style="color:#36acaa">UTC</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cutoff</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getFullYear</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cutoff</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getMonth</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cutoff</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">getDate</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		</span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">anyHave</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">refreshPortfolioRange</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">targetRange</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<a href="https://ammar-najjar.com/blog/portfolio#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="merge-histories">Merge Histories<a href="https://ammar-najjar.com/blog/portfolio#merge-histories" class="hash-link" aria-label="Direct link to Merge Histories" title="Direct link to Merge Histories" translate="no">​</a></h2>
<p>The function below ensures merging two history arrays yields a sorted array without duplicate date entries:</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">mergeHistories</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">oldH</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Candle</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> newH</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Candle</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> Candle</span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> map </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">new</span><span class="token plain"> </span><span class="token class-name">Map</span><span class="token class-name operator" style="color:#393A34">&lt;</span><span class="token class-name builtin">string</span><span class="token class-name punctuation" style="color:#393A34">,</span><span class="token class-name"> </span><span class="token class-name builtin">number</span><span class="token class-name operator" style="color:#393A34">&gt;</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">oldH </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">forEach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> map</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> c</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">value</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">newH </span><span class="token operator" style="color:#393A34">||</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">forEach</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> map</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">set</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">c</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> c</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">value</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">Array</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">from</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">map</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">entries</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">map</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> value</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> time</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> value </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">sort</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">a</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain">b</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> a</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">localeCompare</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">b</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">time</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>The table view shows merged history rows (per-day candles) for each item. <code>mergeHistories</code> ensures rows are unique by date and sorted chronologically; this table is used both for exports and for quick manual inspection when users import JSON.</p>
<p><img decoding="async" loading="lazy" alt="Merged history sample" src="https://ammar-najjar.com/assets/images/table-49f6ab4ae6d889438c000c2c44bcf641.png" width="3392" height="670" class="img_ev3q"></p>
<p><em><em>Merged history table used for export/import and quick inspection.</em></em></p>
<a href="https://ammar-najjar.com/blog/portfolio#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="auto-refresh-and-background-updates">Auto-refresh and background updates<a href="https://ammar-najjar.com/blog/portfolio#auto-refresh-and-background-updates" class="hash-link" aria-label="Direct link to Auto-refresh and background updates" title="Direct link to Auto-refresh and background updates" translate="no">​</a></h2>
<p>The app includes an optional auto-refresh feature that updates quotes and short-range history in the background. Goals for this feature were:</p>
<ul>
<li class="">Keep UI responsive: update data in the background without blocking user interactions.</li>
<li class="">Minimize network usage: only fetch updates for ranges that are considered "fresh" (short ranges like <code>1D</code>/<code>1W</code>) or when items explicitly need a refresh.</li>
<li class="">Avoid redundant requests: respect <code>fetchedRanges</code> and the history-based coverage check described earlier.</li>
</ul>
<p><img decoding="async" loading="lazy" alt="Portfolio header and controls" src="https://ammar-najjar.com/assets/images/header-684887f8ab9a398827693e9fbe8d04ba.png" width="3392" height="242" class="img_ev3q"></p>
<p><em><em>App header and controls — range buttons, add-stock and status indicators (responsive).</em></em></p>
<p>Design notes:</p>
<ul>
<li class="">A lightweight polling loop runs when the app has focus and the user has enabled auto-refresh. It calls a <code>refreshPortfolio()</code> function periodically (configurable interval, e.g. 60s).</li>
<li class=""><code>refreshPortfolio()</code> fetches quotes for all items and optionally fetches short-range history (for example <code>1D</code>) only if the item does not already have a recent timestamp or if <code>fetchedRanges</code> does not include that range.</li>
<li class="">The UI shows a subtle status indicator while background refreshes are in progress; individual components update when the store writes new data.</li>
</ul>
<p>Simplified code snippet (store polling + guarded refresh):</p>
<div class="language-ts codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-ts codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token comment" style="color:#999988;font-style:italic">// in StoreProvider</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">useEffect</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">autoRefreshEnabled</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token keyword" style="color:#00009f">let</span><span class="token plain"> cancelled </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">false</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> </span><span class="token function-variable function" style="color:#d73a49">tick</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">cancelled</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">return</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		</span><span class="token keyword" style="color:#00009f">try</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">			</span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">refreshPortfolio</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> onlyShortRanges</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">catch</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">e</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> </span><span class="token comment" style="color:#999988;font-style:italic">/* swallow non-fatal errors */</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token operator" style="color:#393A34">!</span><span class="token plain">cancelled</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">setTimeout</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">tick</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">AUTO_REFRESH_INTERVAL_MS</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token function" style="color:#d73a49">tick</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> cancelled </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">true</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">autoRefreshEnabled</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token comment" style="color:#999988;font-style:italic">// refreshPortfolio guarded by fetchedRanges</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">async</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">function</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">refreshPortfolio</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">opts</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> onlyShortRanges</span><span class="token operator" style="color:#393A34">?</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token builtin">boolean</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> rangesToRefresh </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> opts</span><span class="token operator" style="color:#393A34">?.</span><span class="token plain">onlyShortRanges </span><span class="token operator" style="color:#393A34">?</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'1D'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token constant" style="color:#36acaa">ALL_RANGES</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> p </span><span class="token keyword" style="color:#00009f">of</span><span class="token plain"> portfolio</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		</span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token keyword" style="color:#00009f">const</span><span class="token plain"> r </span><span class="token keyword" style="color:#00009f">of</span><span class="token plain"> rangesToRefresh</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">			</span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token builtin">Array</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">isArray</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fetchedRanges</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">&amp;&amp;</span><span class="token plain"> p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fetchedRanges</span><span class="token punctuation" style="color:#393A34">.</span><span class="token function" style="color:#d73a49">includes</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">r</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">continue</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">			</span><span class="token keyword" style="color:#00009f">await</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">refreshStock</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">p</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">id</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> r</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">		</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>Notes:</p>
<ul>
<li class="">The polling loop is intentionally conservative: it only requests short-range data by default to keep bandwidth low.</li>
<li class=""><code>refreshStock</code> and <code>refreshPortfolio</code> append the fetched range to <code>fetchedRanges</code> so subsequent cycles skip already-covered ranges.</li>
<li class="">Errors in background refreshes are logged and do not interrupt the UI; a user-facing error flow is only triggered for foreground actions initiated by the user.</li>
</ul>
<a href="https://ammar-najjar.com/blog/portfolio#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="testing-and-verification">Testing and verification<a href="https://ammar-najjar.com/blog/portfolio#testing-and-verification" class="hash-link" aria-label="Direct link to Testing and verification" title="Direct link to Testing and verification" translate="no">​</a></h2>
<ul>
<li class="">The repo uses Vitest + Testing Library for unit tests. Important tests include verifying <code>importPortfolio</code> behavior, <code>refreshPortfolio</code> caching logic, and the updated <code>fetchedRanges</code> persistence for <code>1M</code>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="how-to-run-locally">How to run locally<a href="https://ammar-najjar.com/blog/portfolio#how-to-run-locally" class="hash-link" aria-label="Direct link to How to run locally" title="Direct link to How to run locally" translate="no">​</a></h2>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git clone https://github.com/ammarnajjar/portfolio.git</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">cd portfolio</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npm ci</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">npm run dev</span><br></div></code></pre></div></div>
<a href="https://ammar-najjar.com/blog/portfolio#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Portfolio</category>
            <category>ReactJs</category>
            <category>TypeScript</category>
            <category>Vite</category>
        </item>
        <item>
            <title><![CDATA[Mastering Git Commands: A Practical Guide]]></title>
            <link>https://ammar-najjar.com/blog/mastering-git</link>
            <guid>https://ammar-najjar.com/blog/mastering-git</guid>
            <pubDate>Tue, 08 Jul 2025 20:05:50 GMT</pubDate>
            <description><![CDATA[A practical guide to essential Git commands — configuration, branching, merging, rebasing, stashing, and more — with examples for everyday developer workflows.]]></description>
            <content:encoded><![CDATA[<p>Git is an indispensable tool for version control, enabling developers to track changes, collaborate effectively, and manage project history. While its capabilities are vast, a solid understanding of key commands can significantly streamline your workflow. This article delves into a selection of practical Git commands, explaining what they do, how to use them, and when they are most effective.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-configuration-commands">1. Configuration Commands<a href="https://ammar-najjar.com/blog/mastering-git#1-configuration-commands" class="hash-link" aria-label="Direct link to 1. Configuration Commands" title="Direct link to 1. Configuration Commands" translate="no">​</a></h2>
<p>Setting up your Git environment correctly is the first step. These commands help you define your identity and preferences.</p>
<ul>
<li class="">
<p><strong><code>git config --global user.name "Your Name"</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Sets the global username for your Git commits. This name will appear in the commit history for all repositories on your system.</li>
<li class=""><strong>When to use:</strong> Typically, this is one of the first commands you run after installing Git. It ensures your commits are attributed to you.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git config --global user.name "Ammar Najjar"</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git config --local user.email &lt;email&gt;</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Sets the email address for commits specific to the current repository. This overrides the global email setting.</li>
<li class=""><strong>When to use:</strong> When you need to use a different email address for a particular project (e.g., a work email for a company project, and a personal email for open-source contributions).</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git config --local user.email your.email@example.com</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git config --local user.signingkey &lt;key&gt;</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Configures the GPG key to be used for signing commits locally within a specific repository.</li>
<li class=""><strong>When to use:</strong> If your project or organization requires signed commits for verification and integrity, and you need to use a specific key for that repository.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git config --local user.signingkey YOUR_GPG_KEY_ID</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="understanding-and-using-usersigningkey">Understanding and Using <code>user.signingkey</code><a href="https://ammar-najjar.com/blog/mastering-git#understanding-and-using-usersigningkey" class="hash-link" aria-label="Direct link to understanding-and-using-usersigningkey" title="Direct link to understanding-and-using-usersigningkey" translate="no">​</a></h3>
<p>Commit signing with GPG (GNU Privacy Guard) adds a layer of security and trust to your Git history. When you sign a commit, you cryptographically prove that you are the author of that commit and that the commit's content has not been tampered with since it was signed.</p>
<p><strong>1. Generate a GPG Key (if you don't have one):</strong>
If you don't already have a GPG key, you'll need to generate one. This is a one-time setup.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">gpg --full-generate-key</span><br></div></code></pre></div></div>
<p>Follow the prompts to choose the key type, key size, expiration date, and set a passphrase.</p>
<p><strong>2. Get Your GPG Key ID:</strong>
Once you have a GPG key, you need its ID to tell Git which key to use.</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">gpg --list-secret-keys --keyid-format LONG</span><br></div></code></pre></div></div>
<p>Look for a line similar to <code>sec rsa4096/YOUR_KEY_ID 2023-01-01 [SC]</code> where <code>YOUR_KEY_ID</code> is the 16-character hexadecimal string. This is the ID you'll use.</p>
<p><strong>3. Configure Git to Use Your GPG Key:</strong>
You can configure Git globally or locally for a specific repository.</p>
<ul>
<li class=""><strong>Global Configuration (for all repositories):</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git config --global user.signingkey YOUR_KEY_ID</span><br></div></code></pre></div></div>
</li>
<li class=""><strong>Local Configuration (for the current repository only):</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git config --local user.signingkey YOUR_KEY_ID</span><br></div></code></pre></div></div>
</li>
</ul>
<p><strong>4. Enable Automatic Commit Signing (Optional but Recommended):</strong>
To automatically sign all your commits, you can set a global configuration:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git config --global commit.gpgsign true</span><br></div></code></pre></div></div>
<p>If you don't set this, you'll need to manually sign each commit using <code>git commit -S</code>.</p>
<p><strong>5. Sign a Commit:</strong></p>
<ul>
<li class="">If <code>commit.gpgsign</code> is <code>true</code>, simply run:<!-- -->
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git commit -m "Your commit message"</span><br></div></code></pre></div></div>
<!-- -->You will be prompted for your GPG passphrase.</li>
<li class="">If <code>commit.gpgsign</code> is <code>false</code>, manually sign with:<!-- -->
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git commit -S -m "Your commit message"</span><br></div></code></pre></div></div>
</li>
</ul>
<p><strong>6. Verify a Signed Commit:</strong>
To see if a commit is signed and by whom, use:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git log --show-signature</span><br></div></code></pre></div></div>
<p>You will see a "Good signature from..." message if the signature is valid and the key is trusted.</p>
<ul>
<li class=""><strong><code>git config --global push.default current</code></strong>
<ul>
<li class=""><strong>What it does:</strong> Sets the default behavior for <code>git push</code>. With <code>current</code>, <code>git push</code> will push the current branch to its upstream branch (a branch on the remote repository that it tracks).</li>
<li class=""><strong>When to use:</strong> To simplify your push workflow. Instead of always typing <code>git push origin &lt;branch-name&gt;</code>, you can just type <code>git push</code> and Git will know which remote branch to push to.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git config --global push.default current</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
</ul>
<a href="https://ammar-najjar.com/blog/mastering-git#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-branching-and-navigation">2. Branching and Navigation<a href="https://ammar-najjar.com/blog/mastering-git#2-branching-and-navigation" class="hash-link" aria-label="Direct link to 2. Branching and Navigation" title="Direct link to 2. Branching and Navigation" translate="no">​</a></h2>
<p>Branches are fundamental to Git, allowing parallel development. These commands help you manage and navigate them.</p>
<ul>
<li class="">
<p><strong><code>git rev-parse --abbrev-ref HEAD</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Displays the name of the current branch. <code>HEAD</code> refers to the current commit, and <code>--abbrev-ref</code> shows its symbolic name (the branch name) rather than the full SHA-1 hash.</li>
<li class=""><strong>When to use:</strong> To quickly check which branch you are currently on, especially useful in scripts or when you need to confirm your context.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git rev-parse --abbrev-ref HEAD</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git rev-parse --abbrev-ref HEAD | sed 's/.*\(PATTERN-HERE\).*/\1/'</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Extracts a specific pattern from the current branch name using <code>sed</code>. This is useful for parsing structured branch names (e.g., feature/JIRA-123-description).</li>
<li class=""><strong>When to use:</strong> In scripting or automation where you need to extract specific identifiers (like a ticket number) from your branch name.</li>
<li class=""><strong>How to use:</strong> Replace <code>PATTERN-HERE</code> with your desired regular expression. For example, to extract "JIRA-123" from "feature/JIRA-123-add-feature":<!-- -->
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git rev-parse --abbrev-ref HEAD | sed 's/.*\(JIRA-[0-9]*\).*/\1/'</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git config --global alias.gco = "!f() { git branch -a | grep -e $1 -m 1 | sed 's/remotes\\/origin\\///' | xargs git checkout; }; f"</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Creates a global Git alias named <code>gco</code> that allows you to checkout a branch by providing a partial pattern. It searches all local and remote branches, picks the first match, and checks it out.</li>
<li class=""><strong>When to use:</strong> When you frequently switch between branches and only remember a part of the branch name, or when branch names are long and complex.</li>
<li class=""><strong>How to use:</strong> After setting the alias, you can use it like this:<!-- -->
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git gco PATTERN-HERE</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># Example: git gco feature/my-new-feature (if your branch is feature/my-new-feature-branch)</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git stash branch &lt;branch-name&gt;</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Creates a new branch from the commit that was current when the stash was created, and then applies the stashed changes to this new branch. The stash entry is then dropped.</li>
<li class=""><strong>When to use:</strong> When you've stashed some work, and later decide that those changes should form the basis of a new feature branch, rather than being applied to the current branch.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git stash branch new-feature-branch</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
</ul>
<a href="https://ammar-najjar.com/blog/mastering-git#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-committing-and-history-inspection">3. Committing and History Inspection<a href="https://ammar-najjar.com/blog/mastering-git#3-committing-and-history-inspection" class="hash-link" aria-label="Direct link to 3. Committing and History Inspection" title="Direct link to 3. Committing and History Inspection" translate="no">​</a></h2>
<p>Understanding your project's history and making changes are core Git operations.</p>
<ul>
<li class="">
<p><strong><code>git commit --no-verify</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Creates a commit, but bypasses any pre-commit hooks configured for the repository.</li>
<li class=""><strong>When to use:</strong> Use with caution. This is typically used when a pre-commit hook is temporarily failing or when you know the changes you are committing are intentionally bypassing a specific check (e.g., committing work-in-progress that doesn't yet pass linting).</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git commit --no-verify -m "WIP: Bypassing hooks for now"</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git diff --name-only</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Shows only the names of files that have been changed (staged or unstaged) since the last commit.</li>
<li class=""><strong>When to use:</strong> To quickly get an overview of which files you've modified without seeing the actual content changes.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git diff --name-only</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git log --follow &lt;file&gt;</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Displays the commit history for a specific file, including commits where the file was renamed or moved. Without <code>--follow</code>, <code>git log</code> would stop tracking the file's history if it was renamed.</li>
<li class=""><strong>When to use:</strong> To understand the complete evolution of a file, even if its path has changed over time.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git log --follow src/main.js</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git log -p --follow &lt;file&gt;</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Similar to <code>git log --follow</code>, but also shows the patch (diff) for each commit, detailing the exact changes made to the file.</li>
<li class=""><strong>When to use:</strong> When you need to see not just <em>when</em> a file changed, but <em>what</em> changed in each commit throughout its history, including renames.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git log -p --follow src/main.js</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git show --remerge-diff</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> (Available in Git 2.36.0 and later) When applied to a merge commit, this command shows the diff with merge conflicts and how they were resolved.</li>
<li class=""><strong>When to use:</strong> After a merge, to review the specific changes made to resolve conflicts, providing clarity on the merge strategy.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git show --remerge-diff &lt;merge-commit-hash&gt;</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git log -p -Gword</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Finds commits where the specified "word" (or regular expression) appears in the <em>diff</em> (the actual changes made to the code).</li>
<li class=""><strong>When to use:</strong> To pinpoint commits that introduced or modified a specific piece of code, a variable name, or a function call.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git log -p -G"my_function_name"</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git log --grep=word</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Finds all commits where the commit message contains the specified "word" (or regular expression).</li>
<li class=""><strong>When to use:</strong> To search for commits related to a specific feature, bug fix, or topic based on keywords in their commit messages.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git log --grep="bugfix"</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
</ul>
<a href="https://ammar-najjar.com/blog/mastering-git#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-tagging-and-utilities">4. Tagging and Utilities<a href="https://ammar-najjar.com/blog/mastering-git#4-tagging-and-utilities" class="hash-link" aria-label="Direct link to 4. Tagging and Utilities" title="Direct link to 4. Tagging and Utilities" translate="no">​</a></h2>
<p>Tags are used to mark specific points in history, typically for releases.</p>
<ul>
<li class=""><strong><code>git tag -l | rg -e "2\d.\d.\d$" | sort | tail -n 10</code></strong>
<ul>
<li class=""><strong>What it does:</strong> This is a pipeline of commands:<!-- -->
<ul>
<li class=""><code>git tag -l</code>: Lists all tags in the repository.</li>
<li class=""><code>rg -e "2\d.\d.\d$"</code>: Filters the tags using <code>ripgrep</code> (an external tool) to find those matching a pattern like "2.x.x" (e.g., 2.1.0, 2.36.0).</li>
<li class=""><code>sort</code>: Sorts the filtered tags alphabetically.</li>
<li class=""><code>tail -n 10</code>: Shows the last 10 entries, effectively giving you the 10 most recent "stable" looking tags based on the pattern.</li>
</ul>
</li>
<li class=""><strong>When to use:</strong> To quickly identify recent stable release tags in a repository that follows a semantic versioning scheme.</li>
<li class=""><strong>How to use:</strong> Ensure you have <code>ripgrep</code> installed.<!-- -->
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git tag -l | rg -e "2\d.\d.\d$" | sort | tail -n 10</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
</ul>
<a href="https://ammar-najjar.com/blog/mastering-git#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="5-essential-daily-workflow-commands">5. Essential Daily Workflow Commands<a href="https://ammar-najjar.com/blog/mastering-git#5-essential-daily-workflow-commands" class="hash-link" aria-label="Direct link to 5. Essential Daily Workflow Commands" title="Direct link to 5. Essential Daily Workflow Commands" translate="no">​</a></h2>
<p>These commands are the bread and butter of a software developer's interaction with Git, covering everything from checking status to pushing changes.</p>
<ul>
<li class="">
<p><strong><code>git status</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Shows the status of your working directory and staging area. It tells you which changes have been staged, which haven't, and which files aren't being tracked by Git.</li>
<li class=""><strong>When to use:</strong> Constantly! This is your primary way to understand the current state of your repository before committing or performing other operations.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git status</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git add &lt;file&gt;</code> / <code>git add .</code> / <code>git add -p</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Adds changes from the working directory to the staging area, preparing them for the next commit.<!-- -->
<ul>
<li class=""><code>&lt;file&gt;</code>: Stages specific file(s).</li>
<li class=""><code>.</code>: Stages all changes in the current directory and its subdirectories.</li>
<li class=""><code>-p</code> (patch): Allows you to interactively select specific hunks (parts) of changes within files to stage.</li>
</ul>
</li>
<li class=""><strong>When to use:</strong> Before every commit, to select exactly what changes you want to include. <code>-p</code> is invaluable for crafting clean, focused commits.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git add my_feature.js</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">git add .</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">git add -p</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git commit -m "Your message"</code> / <code>git commit --amend</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Records the staged changes into the repository as a new commit.<!-- -->
<ul>
<li class=""><code>-m</code>: Provides a commit message directly on the command line.</li>
<li class=""><code>--amend</code>: Allows you to modify the most recent commit. You can change its message, add/remove files, or modify existing changes.</li>
</ul>
</li>
<li class=""><strong>When to use:</strong>
<ul>
<li class=""><code>-m</code>: After staging changes, to finalize a logical unit of work.</li>
<li class=""><code>--amend</code>: To fix a typo in the last commit message, or to add a forgotten file/change to the last commit <em>before</em> pushing it to a shared remote.</li>
</ul>
</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git commit -m "Add user authentication feature"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">git commit --amend -m "Fix typo in auth feature commit message"</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git branch &lt;new-branch-name&gt;</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Creates a new branch pointing to the current commit.</li>
<li class=""><strong>When to use:</strong> To start working on a new feature, bug fix, or experiment without affecting the main codebase.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git branch feature/new-dashboard</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git switch &lt;branch-name&gt;</code> / <code>git checkout &lt;branch-name&gt;</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Switches your working directory to the specified branch. <code>git switch</code> is the newer, safer command for changing branches, while <code>git checkout</code> is more versatile (can also restore files).</li>
<li class=""><strong>When to use:</strong> To move between different lines of development.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git switch main</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">git checkout feature/new-dashboard # Older but still common</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git checkout -b &lt;new-branch-name&gt;</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> A convenient shortcut that creates a new branch and immediately switches to it.</li>
<li class=""><strong>When to use:</strong> When you want to start a new feature branch and jump right into it.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git checkout -b bugfix/login-issue</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git pull</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Fetches changes from the remote repository and automatically merges them into your current branch. It's a shortcut for <code>git fetch</code> followed by <code>git merge</code>.</li>
<li class=""><strong>When to use:</strong> To synchronize your local branch with its upstream remote counterpart, pulling down the latest changes from collaborators.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git pull origin main</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># Or simply: git pull (if upstream is set)</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git fetch</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Downloads objects and refs from another repository (the remote) into your local repository. It <em>does not</em> merge or modify your local working files.</li>
<li class=""><strong>When to use:</strong> To see what changes have occurred on the remote without integrating them into your current work. Useful for reviewing remote branches or preparing for a merge/rebase.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git fetch origin</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git push</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Uploads your local commits to the remote repository.</li>
<li class=""><strong>When to use:</strong> To share your completed work with others or to back up your changes.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git push origin feature/my-new-feature</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"># Or simply: git push (if upstream is set)</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git merge &lt;branch&gt;</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Integrates changes from the specified branch into your current branch.</li>
<li class=""><strong>When to use:</strong> To combine completed feature branches into a main development branch, or to pull changes from a shared branch into your own.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git switch main</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">git merge feature/my-new-feature</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git rebase &lt;branch&gt;</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Reapplies your current branch's commits on top of another base branch. This rewrites your branch's history, creating a linear history.</li>
<li class=""><strong>When to use:</strong> To keep your feature branch up-to-date with the main branch without creating merge commits, resulting in a cleaner, linear history. Use with caution on branches that have already been pushed and shared.</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git switch feature/my-new-feature</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">git rebase main</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>git reset --hard HEAD~1</code> / <code>git revert HEAD</code></strong></p>
<ul>
<li class=""><strong>What it does:</strong> Both undo changes, but in different ways:<!-- -->
<ul>
<li class=""><code>git reset --hard HEAD~1</code>: Moves the current branch pointer back one commit, discarding any changes in the working directory and staging area. <strong>This rewrites history.</strong></li>
<li class=""><code>git revert HEAD</code>: Creates a <em>new</em> commit that undoes the changes introduced by the specified commit. <strong>This preserves history.</strong></li>
</ul>
</li>
<li class=""><strong>When to use:</strong>
<ul>
<li class=""><code>git reset --hard</code>: To completely discard the last commit (and any uncommitted changes) <em>locally</em>, especially if you haven't pushed it yet. Use with extreme caution as it loses data.</li>
<li class=""><code>git revert</code>: To undo a commit that has already been pushed to a shared remote, as it creates a new commit that explicitly reverses the changes, maintaining a clear history.</li>
</ul>
</li>
<li class=""><strong>How to use:</strong>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git reset --hard HEAD~1 # Discard last commit</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">git revert HEAD         # Create a new commit that undoes the last one</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
<li class="">
<p><strong><code>.gitignore</code> file</strong></p>
<ul>
<li class=""><strong>What it does:</strong> A plain text file in your repository's root directory that tells Git which files or directories to ignore. Git will not track these files, nor will it show them as untracked.</li>
<li class=""><strong>When to use:</strong> To prevent unnecessary files (like build artifacts, temporary files, IDE configuration, or sensitive credentials) from being committed to the repository.</li>
<li class=""><strong>How to use:</strong> Create a file named <code>.gitignore</code> in your project root and list patterns for files/folders to ignore.<!-- -->
<div class="language-text codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-text codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain"># Example .gitignore content</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">/node_modules/</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">.env</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">*.log</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">build/</span><br></div></code></pre></div></div>
</li>
</ul>
</li>
</ul>
<hr>
<a href="https://ammar-najjar.com/blog/mastering-git#" class="go-to-top">Go to Top</a>
<p>These commands represent a powerful subset of Git's capabilities. By understanding their purpose and knowing when to apply them, you can navigate your project's history, manage branches, and collaborate more efficiently. Regular practice with these commands will solidify your Git proficiency and enhance your development workflow.</p>]]></content:encoded>
            <category>Git</category>
        </item>
        <item>
            <title><![CDATA[Book Review - Atomic Habits]]></title>
            <link>https://ammar-najjar.com/blog/atomic-habits</link>
            <guid>https://ammar-najjar.com/blog/atomic-habits</guid>
            <pubDate>Tue, 24 Jun 2025 15:55:50 GMT</pubDate>
            <description><![CDATA[Book review of Atomic Habits by James Clear — the Four Laws of Behavior Change, identity-based habits, and why systems beat goals for lasting personal improvement.]]></description>
            <content:encoded><![CDATA[<p>In his book "Atomic Habits", James Clear outlines the principles of habit formation and behavior change. The author shares his personal journey of recovery from a severe injury, which led him to understand that significant progress stems from a series of small, consistent actions, rather than singular transformative events. Clear introduces the "Four Laws of Behavior Change": making habits obvious, attractive, easy, and satisfying. He emphasizes the importance of systems over goals, highlighting how environmental design, social influence, and the immediate gratification of rewards play crucial roles in establishing and maintaining habits, ultimately arguing that true identity change is the North Star of habit transformation.</p>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="summery">Summery:<a href="https://ammar-najjar.com/blog/atomic-habits#summery" class="hash-link" aria-label="Direct link to Summery:" title="Direct link to Summery:" translate="no">​</a></h3>
<p>The central theme revolves around the idea that "success is the product of daily habits—not once-in-a-lifetime transformations". It emphasizes the compounding effects of small, consistent improvements and the importance of systemic thinking over goal-setting.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="i-the-power-of-atomic-habits-compounding-small-improvements">I. The Power of "Atomic Habits": Compounding Small Improvements<a href="https://ammar-najjar.com/blog/atomic-habits#i-the-power-of-atomic-habits-compounding-small-improvements" class="hash-link" aria-label="Direct link to I. The Power of &quot;Atomic Habits&quot;: Compounding Small Improvements" title="Direct link to I. The Power of &quot;Atomic Habits&quot;: Compounding Small Improvements" translate="no">​</a></h3>
<p>The fundamental premise of "Atomic Habits" is that minuscule, consistent changes, referred to as "atomic habits," accumulate over time to produce significant, even "remarkable," results.</p>
<ul>
<li class=""><strong>Compound Interest of Self-Improvement</strong>: Habits are likened to "the compound interest of self-improvement". Just as money multiplies with compound interest, "the effects of your habits multiply as you repeat them".</li>
<li class=""><strong>The 1% Rule</strong>: Getting "just 1 percent better each day, you’ll end up with results that are nearly 37 times better after one year". Conversely, being 1% worse leads to a near-zero outcome (0.99^365 = 0.03). This small difference "can make over time is astounding".</li>
</ul>
<p><img decoding="async" loading="lazy" alt="1%" src="https://ammar-najjar.com/assets/images/1percent-89f6bbc8bf354d7801174418ba5a5d2f.jpg" width="1872" height="1504" class="img_ev3q"></p>
<ul>
<li class=""><strong>Delayed Outcomes</strong>: The impact of small habits is often not immediately apparent. "It is only when looking back two, five, or perhaps ten years later that the value of good habits and the cost of bad ones becomes strikingly apparent". This requires patience, as "Change can take years—before it happens all at once," echoing the stonecutter analogy: "Yet at the hundred and first blow it will split in two, and I know it was not that last blow that did it—but all that had gone before". This period of unnoticeable progress before breakthroughs is called the "Plateau of Latent Potential". People often stop making changes because they don't see immediate tangible results, not realizing their work is being stored.</li>
<li class=""><strong>Trajectory Over Current Results</strong>: What truly matters is the "current trajectory" set by your habits, rather than "current results". Your "outcomes are a lagging measure of your habits". For example, your net worth is a lagging measure of your financial habits, and your weight is a lagging measure of your eating habits. Tiny daily choices "will compound ten or twenty years down the line". Good habits make time your ally, while bad habits make time your enemy.</li>
<li class=""><strong>Double-Edged Sword</strong>: Habits are a "double-edged sword". They can either compound for you or against you. For instance, productivity and knowledge compound positively, as does being nicer in interactions leading to stronger relationships. Conversely, stress and negative thoughts can compound into serious issues.</li>
<li class=""><strong>Atomic Habit Defined</strong>: An "atomic habit" refers to a tiny change, a marginal gain, or a 1 percent improvement, and they are "little habits that are part of a larger system". Just as atoms are the building blocks of molecules, "atomic habits are the building blocks of remarkable results". They are small and easy to do, yet also "the source of incredible power".</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="ii-systems-over-goals-the-foundation-for-lasting-change">II. Systems Over Goals: The Foundation for Lasting Change<a href="https://ammar-najjar.com/blog/atomic-habits#ii-systems-over-goals-the-foundation-for-lasting-change" class="hash-link" aria-label="Direct link to II. Systems Over Goals: The Foundation for Lasting Change" title="Direct link to II. Systems Over Goals: The Foundation for Lasting Change" translate="no">​</a></h2>
<p>A pivotal concept introduced is the distinction between goals and systems, advocating for a focus on the latter for sustained progress.</p>
<ul>
<li class=""><strong>Goals vs. Systems</strong>: "Goals are about the results you want to achieve. Systems are about the processes that lead to those results". The author argues that focusing solely on goals often leads to failure because the underlying "system" for change is flawed.</li>
<li class=""><strong>Falling to the Level of Your Systems</strong>: "You do not rise to the level of your goals. You fall to the level of your systems". This highlights that consistent behavior is a direct output of the established processes, not merely aspirations.</li>
<li class=""><strong>Goal-less Thinking</strong>: True long-term thinking is described as "goal-less thinking". It's about <strong>"the cycle of endless refinement and continuous improvement,"</strong> and <strong>"your commitment to the process that will determine your progress"</strong>.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="iii-identity-based-habits-changing-who-you-are">III. Identity-Based Habits: Changing Who You Are<a href="https://ammar-najjar.com/blog/atomic-habits#iii-identity-based-habits-changing-who-you-are" class="hash-link" aria-label="Direct link to III. Identity-Based Habits: Changing Who You Are" title="Direct link to III. Identity-Based Habits: Changing Who You Are" translate="no">​</a></h2>
<p>Beyond merely changing behaviors, "Atomic Habits" emphasizes that <strong>true, lasting habit change stems from a shift in identity</strong>.</p>
<ul>
<li class=""><strong>Behavior Change as Identity Change</strong>: "True behavior change is identity change". Instead of focusing on what you want to achieve (outcome-based goals) or what you need to do (process-based habits), the most profound change comes from focusing on <strong>"who you wish to become"</strong>.</li>
<li class=""><strong>"Repeated Beingness"</strong>: Your identity is literally your "repeated beingness," formed by the actions you consistently take. Every action is a <strong>"vote for the type of person you wish to become"</strong>.</li>
<li class=""><strong>Proof Through Small Wins</strong>: To build a new identity, one must "Prove it to yourself with small wins". For instance, "Each time you write a page, you are a writer. Each time you practice the violin, you are a musician".</li>
<li class=""><strong>Pride and Motivation</strong>: "The more pride you have in a particular aspect of your identity, the more motivated you will be to maintain the habits associated with it". The example of Clark stopping nail-biting after taking pride in his manicured nails illustrates this point.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="iv-the-four-laws-of-behavior-change">IV. The Four Laws of Behavior Change<a href="https://ammar-najjar.com/blog/atomic-habits#iv-the-four-laws-of-behavior-change" class="hash-link" aria-label="Direct link to IV. The Four Laws of Behavior Change" title="Direct link to IV. The Four Laws of Behavior Change" translate="no">​</a></h2>
<p>The author presents <strong>four fundamental laws for building good habits and breaking bad ones</strong>. These laws offer a framework for designing an environment conducive to desired behaviors.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="1-make-it-obvious-1st-law">1. Make it Obvious (1st Law):<a href="https://ammar-najjar.com/blog/atomic-habits#1-make-it-obvious-1st-law" class="hash-link" aria-label="Direct link to 1. Make it Obvious (1st Law):" title="Direct link to 1. Make it Obvious (1st Law):" translate="no">​</a></h3>
<ul>
<li class=""><strong>Awareness is Key</strong>: Habits are often "nonconscious and automatic". To improve them, you must first become aware of them. "Until you make the unconscious conscious, it will direct your life and you will call it fate".</li>
<li class=""><strong>The Habits Scorecard</strong>: A practical tool to identify and categorize existing habits (+ for good, – for bad, = for neutral) by listing daily behaviors and assessing their long-term impact. The goal is to <strong>"simply notice what is actually going on" without judgment</strong>.</li>
<li class=""><strong>Implementation Intentions</strong>: Clearly defining <em>when</em> and <em>where</em> a habit will occur: <strong>"I will [BEHAVIOR] at [TIME] in [LOCATION]"</strong>. This specificity helps in saying "no to things that derail progress".</li>
<li class=""><strong>Habit Stacking</strong>: Linking a new habit to an existing one: <strong>"After [CURRENT HABIT], I will [NEW HABIT]"</strong>. This leverages established routines as cues.</li>
<li class=""><strong>Environmental Design</strong>: Make cues for good habits visible and accessible (e.g., placing workout clothes out). Conversely, make cues for bad habits invisible (e.g., removing junk food from sight). "We like to think that we are in control... but the truth... is that many of the actions we take each day are shaped... by the most obvious option".</li>
<li class=""><strong>Priming the Environment</strong>: "Resetting the room" (e.g., cleaning up after an activity) prepares the environment for the next desired action, making it easy to start.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="2-make-it-attractive-2nd-law">2. Make it Attractive (2nd Law):<a href="https://ammar-najjar.com/blog/atomic-habits#2-make-it-attractive-2nd-law" class="hash-link" aria-label="Direct link to 2. Make it Attractive (2nd Law):" title="Direct link to 2. Make it Attractive (2nd Law):" translate="no">​</a></h3>
<ul>
<li class=""><strong>Dopamine-Driven Feedback Loop</strong>: Habits are deeply tied to dopamine, a neurotransmitter associated with craving and desire. "When dopamine rises, so does our motivation to act". It's the <strong>"anticipation of a reward—not the fulfillment of it—that gets us to take action"</strong>.</li>
<li class=""><strong>Supernormal Stimuli</strong>: Humans are naturally drawn to "heightened versions of reality" (like hyper-palatable processed foods) that amplify desire.</li>
<li class=""><strong>Temptation Bundling</strong>: Pair an action you <em>need</em> to do with an action you <em>want</em> to do. Formula: <strong>"After [HABIT I NEED], I will [HABIT I WANT]"</strong> (e.g., Ronan Byrne connecting Netflix to his stationary bike).</li>
<li class=""><strong>Social Influence</strong>: We are heavily influenced by the habits of those around us, especially "the close, the many, and the powerful". "One of the most effective things you can do to build better habits is to join a culture where your desired behavior is the normal behavior".</li>
<li class=""><strong>Reframing</strong>: Change your mindset about habits by highlighting their benefits or reframing negative associations. "You don’t 'have' to. You 'get' to" (e.g., "I get to run in the morning" instead of "I have to run"). This relates to the inverse: "make it unattractive" (e.g., Allen Carr's reframing of smoking).</li>
<li class=""><strong>Underlying Motives</strong>: Cravings are specific manifestations of deeper, underlying human motives (e.g., to conserve energy, obtain food, connect with others, reduce uncertainty, gain status). Understanding these deeper desires can help in making habits attractive.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="3-make-it-easy-3rd-law">3. Make it Easy (3rd Law):<a href="https://ammar-najjar.com/blog/atomic-habits#3-make-it-easy-3rd-law" class="hash-link" aria-label="Direct link to 3. Make it Easy (3rd Law):" title="Direct link to 3. Make it Easy (3rd Law):" translate="no">​</a></h3>
<ul>
<li class=""><strong>Reduce Friction</strong>: Make good habits effortless and bad habits difficult. "The greater the friction, the less likely the habit".</li>
<li class=""><strong>The Law of Least Effort</strong>: Human nature seeks the path of least resistance. Design your environment to make desired actions the easiest option.</li>
<li class=""><strong>"Master the Habit of Showing Up"</strong>: Don't focus on perfecting a habit from the start; instead, "do the easy thing on a more consistent basis". "A habit must be established before it can be improved".</li>
<li class=""><strong>The Two-Minute Rule</strong>: <strong>"When you start a new habit, it should take less than two minutes to do"</strong>. This minimizes the initial barrier to action, ensuring you "master the habit of showing up".</li>
<li class=""><strong>Commitment Devices</strong>: Make choices in the present that control your future actions, binding you to good habits and restricting bad ones (e.g., Victor Hugo locking away his clothes to write, Nir Eyal's internet router timer). These devices "enable you to take advantage of good intentions before you can fall victim to temptation".</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="4-make-it-satisfying-4th-law">4. Make it Satisfying (4th Law):<a href="https://ammar-najjar.com/blog/atomic-habits#4-make-it-satisfying-4th-law" class="hash-link" aria-label="Direct link to 4. Make it Satisfying (4th Law):" title="Direct link to 4. Make it Satisfying (4th Law):" translate="no">​</a></h3>
<ul>
<li class=""><strong>Immediate Gratification</strong>: The brain prioritizes immediate pleasure. Behaviors followed by "feelings of pleasure" are signals to the brain to "Do this again, next time".</li>
<li class=""><strong>The Cardinal Rule of Behavior Change</strong>: <strong>"What is rewarded is repeated. What is punished is avoided"</strong>.</li>
<li class=""><strong>Instant vs. Delayed Rewards</strong>: Many good habits have delayed rewards, while bad habits often provide instant gratification. This is known as "time inconsistency" or "hyperbolic discounting". "The more immediate pleasure you get from an action, the more strongly you should question whether it aligns with your long-term goals".</li>
<li class=""><strong>Habit Tracking</strong>: A simple way to make the progress of good habits visible and satisfying. Tools like the "Paper Clip Strategy" or crossing off a calendar day provide immediate visual proof of progress, creating a "habit streak" that motivates continued action.</li>
<li class=""><strong>Never Miss Twice</strong>: The critical rule for maintaining streaks: <strong>"If I miss one day, I try to get back into it as quickly as possible. Missing once is an accident. Missing twice is the start of a new habit"</strong>.</li>
<li class=""><strong>Habit Contracts</strong>: Formal agreements with accountability partners that outline a commitment to a habit and specify punishments for non-adherence, leveraging the desire for social approval and avoiding negative consequences.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="v-additional-insights">V. Additional Insights<a href="https://ammar-najjar.com/blog/atomic-habits#v-additional-insights" class="hash-link" aria-label="Direct link to V. Additional Insights" title="Direct link to V. Additional Insights" translate="no">​</a></h2>
<ul>
<li class=""><strong>Genetic Predisposition</strong>: Genes influence nearly "every behavior". While they don't dictate destiny, they "predispose you to a unique cluster of genetic traits" that can make certain habits naturally easier or harder. The goal is to "identify the opportunities and habits that are right for you" based on your personality.</li>
<li class=""><strong>The Plateau of Latent Potential</strong>: There's often a period of unnoticeable progress before breakthroughs occur. Patience is crucial to overcome this "Valley of Disappointment".</li>
</ul>
<p><img decoding="async" loading="lazy" alt="valley of disappointment" src="https://ammar-najjar.com/assets/images/valley-of-disappointment-39e0f56e083bd1235bb115910f645a15.jpg" width="1296" height="880" class="img_ev3q"></p>
<ul>
<li class=""><strong>The Role of Emotion and Desire</strong>: "Desire is the difference between where you are now and where you want to be in the future". Conversely, "Happiness is simply the absence of desire... It arrives when you have no urge to feel differently". This suggests that observing actions without craving a change in state leads to contentment.</li>
<li class=""><strong>Brain Plasticity</strong>: Repetition of habits leads to "clear physical changes in the brain," akin to muscles adapting to training. <strong>"Neurons that fire together wire together"</strong>.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="highlights">Highlights<a href="https://ammar-najjar.com/blog/atomic-habits#highlights" class="hash-link" aria-label="Direct link to Highlights" title="Direct link to Highlights" translate="no">​</a></h3>
<ul>
<li class="">
<p>➕ Small Habits Compound<br>
<!-- -->A 1% improvement every day leads to 37x improvement in a year.
Bad habits compound negatively in the same way.</p>
</li>
<li class="">
<p>🛠️ Systems vs. Goals<br>
<!-- -->Don’t just set goals—build systems that get you there.
Winners and losers often have the same goals. Systems make the difference.</p>
</li>
<li class="">
<p>🧩 Identity-Based Habits<br>
<!-- -->Focus on who you want to become, not just what you want to achieve.
“The goal is not to read a book, but to become a reader.”
Every action is a vote for the kind of person you want to be.</p>
</li>
<li class="">
<p>🔁 Cue → Craving → Response → Reward<br>
<!-- -->This habit loop explains how behaviors are formed and maintained.</p>
</li>
<li class="">
<p>⚖️ Good vs. Bad Habit Loop<br>
<!-- -->Good habits: usually feel bad now but benefit long-term.
Bad habits: feel good now but harm long-term.
Key insight: align short-term satisfaction with long-term benefits.</p>
</li>
<li class="">
<p>🎯 Motivation ≠ Discipline<br>
<!-- -->It’s not about willpower. It's about environment and design.
People with strong self-control are better at avoiding temptation, not resisting it.</p>
</li>
<li class="">
<p>📈 Mastery through Deliberate Practice<br>
<!-- -->Combine automaticity (via habits) with intentional improvement.
Use reflection and review to maintain self-awareness and avoid complacency.</p>
</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="opinion">Opinion<a href="https://ammar-najjar.com/blog/atomic-habits#opinion" class="hash-link" aria-label="Direct link to Opinion" title="Direct link to Opinion" translate="no">​</a></h3>
<p>In essence, "Atomic Habits" provides a systematic framework for understanding and manipulating the forces that drive human behavior, enabling individuals to cultivate habits that align with their long-term aspirations by making them obvious, attractive, easy, and satisfying.</p>
<p>I really liked this book, read it multiple times.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://ammar-najjar.com/blog/atomic-habits#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h3>
<ul>
<li class="">Success = Systems + Tiny Improvements ✅</li>
<li class="">I would recommend it to everyone 👍</li>
</ul>
<a href="https://ammar-najjar.com/blog/atomic-habits#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Productivity</category>
            <category>Book Review</category>
        </item>
        <item>
            <title><![CDATA[Angular - Content Projection]]></title>
            <link>https://ammar-najjar.com/blog/angular-content-projection</link>
            <guid>https://ammar-najjar.com/blog/angular-content-projection</guid>
            <pubDate>Sun, 20 Jun 2021 11:05:50 GMT</pubDate>
            <description><![CDATA[A guide to Angular content projection — the different types (single-slot, multi-slot, conditional), when to use each, and how to build flexible, reusable components.]]></description>
            <content:encoded><![CDATA[<p>Content projection is a feature in angular that supports inserting a component in another component. This changes the way an angular developer designs his application/library by implementing flexable and reusable components.</p>
<p>This post explains the different types of content projection, where to use each of them, with an example project. All the code can be found on github.(LINK)</p>
<!-- -->
<p>Types of Content projection:</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="single-slot-content-projection">Single-slot Content Projection<a href="https://ammar-najjar.com/blog/angular-content-projection#single-slot-content-projection" class="hash-link" aria-label="Direct link to Single-slot Content Projection" title="Direct link to Single-slot Content Projection" translate="no">​</a></h2>
<p>In this type, only one slot is avaiable for a component to be inserted in the container component.</p>
<p>For example, let's assume that you have a footer component, which has to include some sort of content (child) component. There are many scenarios where using content projection here right from the start:</p>
<ul>
<li class="">The content component is not yet defined. It could be a button, a text label, a select, a link, an icon or an image.</li>
<li class="">You work in a team, where you split the job, and you implement the footer component, meanwhile another developer will implement the content component.</li>
<li class="">The content component is defined, and you work on the footer alone, but you want to make sure that these two components are not tightly coupled, so it is possible to switch either one of them without the need to re-implement or modify any of them.</li>
</ul>
<p>The footer component is given a slot where a content component can be inserted using <code>ng-content</code>:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> Component </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@angular/core'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token decorator at operator" style="color:#393A34">@</span><span class="token decorator function" style="color:#d73a49">Component</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  selector</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'app-footer'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  template</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c"></span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">  &lt;div class="footer"&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    &lt;ng-content&gt;&lt;/ng-content&gt;   // content slot</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">  &lt;/div&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c"></span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">FooterComponent</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>The content component can be any thing, and for the sake of keeping the focus on the content projectoin theme here, let's assume that it is just an "about" button:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> Component </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@angular/core'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token decorator at operator" style="color:#393A34">@</span><span class="token decorator function" style="color:#d73a49">Component</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  selector</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'app-button'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  template</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c"></span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    &lt;button&gt;about&lt;/button&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">  </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">ButtonComponent</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>The footer component can be used now inside the app component, and the button component can be inserted/projected in place:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> Component </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@angular/core'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token decorator at operator" style="color:#393A34">@</span><span class="token decorator function" style="color:#d73a49">Component</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  selector</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'app-root'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  template</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c"></span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    &lt;app-footer&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">      &lt;app-button&gt;&lt;/app-button&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    &lt;/app-footer&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">  </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">AppComponent</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>That's it.</p>
<p>If the requirements changes, and the footer is needed to have a link text instead of the button, all what needs to be done is to implement the new content component, then project it in place in the footer component instead of the button component.</p>
<a href="https://ammar-najjar.com/blog/angular-content-projection#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="multi-slot-content-projection">Multi-slot content projection:<a href="https://ammar-najjar.com/blog/angular-content-projection#multi-slot-content-projection" class="hash-link" aria-label="Direct link to Multi-slot content projection:" title="Direct link to Multi-slot content projection:" translate="no">​</a></h2>
<p>This type of projection allows the insertion of multiple content components in the container component. That means that there are more than one <code>ng-content</code> slot.</p>
<p>Let's continue with the previous footer example, and assume that the requirements changed again, and the footer is required to have both a button on the left and a text link on the right at the same time. Footer component, a button coponent and a link text component are already there.</p>
<p>In order to project multiple contents, the footer component needs to be updated, and let it accept two slots in this case, and each slot takes a <code>selelct</code> attribute which determine which content to be rendered in which slot:</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> Component </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@angular/core'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token decorator at operator" style="color:#393A34">@</span><span class="token decorator function" style="color:#d73a49">Component</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  selector</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'app-footer'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  style</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c"></span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    .footer {</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">      display: flex;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">      justify-content: space-between;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    }</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">  </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  template</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c"></span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">  &lt;div class="footer"&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    &lt;ng-content select=[left]&gt;&lt;/ng-content&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    &lt;ng-content select=[right]&gt;&lt;/ng-content&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">  &lt;/div&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c"></span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">FooterComponent</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>These attributes (<code>left</code>, <code>right</code>) define which content where to render, so to meet the requirement, the button should be given the <code>left</code> attribute, and the link component should be given the <code>right</code> attribute.</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"> Component </span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">from</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'@angular/core'</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token decorator at operator" style="color:#393A34">@</span><span class="token decorator function" style="color:#d73a49">Component</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  selector</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'app-button'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  template</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c"></span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    &lt;button&gt;about&lt;/button&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">  </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">ButtonComponent</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token decorator at operator" style="color:#393A34">@</span><span class="token decorator function" style="color:#d73a49">Component</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  selector</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'app-link'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  template</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c"></span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    &lt;a href="#"&gt;about&lt;/a&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">  </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">LinkComponent</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token decorator at operator" style="color:#393A34">@</span><span class="token decorator function" style="color:#d73a49">Component</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  selector</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'app-root'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  template</span><span class="token operator" style="color:#393A34">:</span><span class="token plain"> </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token template-string string" style="color:#e3116c"></span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    &lt;app-footer&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">      &lt;app-button left&gt;&lt;/app-button&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">      &lt;app-link right&gt;&lt;/app-link&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">    &lt;/app-footer&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token template-string string" style="color:#e3116c">  </span><span class="token template-string template-punctuation string" style="color:#e3116c">`</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">export</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">AppComponent</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<p>This implementation gives a huge felxibility and decoupling of components. For example it is very easy to switch the content components places, or even replace any of the content components without the need to re-implement the whole structure.</p>
<a href="https://ammar-najjar.com/blog/angular-content-projection#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conditional-content-projection">Conditional content projection:<a href="https://ammar-najjar.com/blog/angular-content-projection#conditional-content-projection" class="hash-link" aria-label="Direct link to Conditional content projection:" title="Direct link to Conditional content projection:" translate="no">​</a></h2>
<p>In this type of projection, a condition coverns which content to be rendered in the container component slot. This condition can be made using an <code>ngIf</code> statement.</p>
<p>In order to use conditional content projection, <code>ng-container</code> and <code>ng-template</code> must be used.</p>
<p>By using <code>ng-content</code>, angular will <strong>always</strong> initialize the content component even if it was inside an <code>ngIf</code> statement.</p>
<p>By using <code>ng-container</code> or <code>ng-template</code>, angular will only render the content component only if it is explicitly asked to render.</p>
<p>Let's go again to the footer compoennt example, and modify the requirement this time so that only one content must be show at a time, depending on some condition.</p>
<p>To apply these requirements, the change needs to happens</p>
<div class="language-typescript codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-typescript codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token decorator at operator" style="color:#393A34">@</span><span class="token decorator function" style="color:#d73a49">HostListener</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'window:resize'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'$event'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token function" style="color:#d73a49">onResize</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">event</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  </span><span class="token keyword" style="color:#00009f">this</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">innerWidth </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> window</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">innerWidth</span><span class="token punctuation" style="color:#393A34">;</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<a href="https://ammar-najjar.com/blog/angular-content-projection#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Angular</category>
        </item>
        <item>
            <title><![CDATA[Reactjs: Arrays in jsx]]></title>
            <link>https://ammar-najjar.com/blog/arrays-in-jsx</link>
            <guid>https://ammar-najjar.com/blog/arrays-in-jsx</guid>
            <pubDate>Sun, 24 Nov 2019 20:03:52 GMT</pubDate>
            <description><![CDATA[How to correctly render arrays in React JSX — why repeating elements is a problem, and how to use map() with keys to build lists cleanly.]]></description>
            <content:encoded><![CDATA[<p>When I tried out Reactjs <a href="https://reactjs.org/tutorial/tutorial.html" target="_blank" rel="noopener noreferrer" class="">tutorial</a> few months ago, I noticed that in the official <a href="https://codepen.io/gaearon/pen/oWWQNa?editors=0010" target="_blank" rel="noopener noreferrer" class="">starter code</a>, they repeated same <code>div</code> elements in the <code>render()</code> method, just in case of change in the source, I will list it here:</p>
<!-- -->
<iframe width="100%" frameborder="0" id="gist-a607a290ddf165d8ab49bac2a902caec-original.jsx"></iframe>
<p>From the fist look I did not like the repetition, even for a beginner in React, javascript basic array operations should be a familiar topic.</p>
<p>So I refactored that section using the <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Array/map" target="_blank" rel="noopener noreferrer" class=""><code>map</code></a> operation:</p>
<iframe width="100%" frameborder="0" id="gist-a607a290ddf165d8ab49bac2a902caec-using_map.jsx"></iframe>
<p>I do not think that this is so complecated to be understood for any React beginner, and I would recommend using similar concise code instead of repeating the same function calls or elements over and over again.</p>
<p>What do you think?</p>
<a href="https://ammar-najjar.com/blog/arrays-in-jsx#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>ReactJs</category>
        </item>
        <item>
            <title><![CDATA[PyConDe & PyData 2019]]></title>
            <link>https://ammar-najjar.com/blog/pyconde-pydata-2019</link>
            <guid>https://ammar-najjar.com/blog/pyconde-pydata-2019</guid>
            <pubDate>Tue, 15 Oct 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[Personal notes from attending PyConDe and PyData Berlin 2019 — talks, workshops, and highlights from the Python and data science community conference.]]></description>
            <content:encoded><![CDATA[<p><a href="https://de.pycon.org/" target="_blank" rel="noopener noreferrer" class=""><img src="https://ammar-najjar.com/assets/images/PyConDEPyDataBER-85c5b5c9a84079a82df243a994bf35b9.jpg" alt="pyconde-logo" max-width="700" maxheight="200" width="100%" height="100%"></a></p>
<p>I have just came back from <a href="https://de.pycon.org/" target="_blank" rel="noopener noreferrer" class="">PyConDe &amp; PyData-2019</a>, and I would like to share/document my experience there. This is more like a diary than a blog, where I talk about:</p>
<ul>
<li class="">Arriving</li>
<li class="">First Day</li>
<li class="">Second Day</li>
<li class="">Third Day</li>
<li class="">Sprint Days</li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="arriving">Arriving:<a href="https://ammar-najjar.com/blog/pyconde-pydata-2019#arriving" class="hash-link" aria-label="Direct link to Arriving:" title="Direct link to Arriving:" translate="no">​</a></h2>
<p>I drove about five hours on the 8th of October 2019, a day before the event,
to get a hotel in the neighborhood of the event venue <a href="https://www.kosmos-berlin.de/" target="_blank" rel="noopener noreferrer" class="">Kosmos Berlin</a>.
It is always good to stay near the conference venue, trust me, it is worth it to pay extra just for
this reason alone.</p>
<p>I arrived in the evening, head the shower, then prepared the schedule for talks/tutorials I want to attend to, then went to sleep realy excited for the next day.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="first-day">First Day<a href="https://ammar-najjar.com/blog/pyconde-pydata-2019#first-day" class="hash-link" aria-label="Direct link to First Day" title="Direct link to First Day" translate="no">​</a></h2>
<p>Doors were supposed to open at 9 am. I walked to the venue (about 5 minutes) to arrive few minute before the openning. They openend on time, and people started comming in. Directly after the main door, there were tables labeled with letters, and one must pick up the enterance padge from there acourding to thier first name letter.
I found this nice and well organized. Thumbs up PyConDe!</p>
<p>After the main door, is a hall, with an open coffee bar in the center, and sponsers booth all around.
Also +1 for the decoration.</p>
<p>Wifi was available for all, and there were a separate wifi for tutorials, where people could <code>pip install</code> stuff easily.</p>
<p>I attended:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="talks">Talks:<a href="https://ammar-najjar.com/blog/pyconde-pydata-2019#talks" class="hash-link" aria-label="Direct link to Talks:" title="Direct link to Talks:" translate="no">​</a></h3>
<ul>
<li class="">Keynote: Ethics with coding: Avery interesting talk about decision making systems, and highly recommended to watch for both technical and non-technical people.</li>
<li class="">Developers vs Enterprise: Nice overview about what roles are really involved in big projects,
how they affect the project goal. If you are already working in a big company/project, this talk would be old news, but if you are not, and want to change jobs to a big company, this is worth watching.</li>
<li class="">Running an Open source project: Nice experience overview for a joung maintainer, who started a python library on github, and how she was managing/marketing/developing all at the same time. Recommended if you intend to start something similar. Actually I kinda did similar thigs, except I don't have an idea for a general purpose library.. yet.</li>
<li class="">Migrating CPython to github: very interesting to see the challenges of migrating the core development of CPython from whatever they are currently using to github. I would never imagine that there were a core python developer who is not familiar with working with github! I was surprised.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="tutorials">Tutorials:<a href="https://ammar-najjar.com/blog/pyconde-pydata-2019#tutorials" class="hash-link" aria-label="Direct link to Tutorials:" title="Direct link to Tutorials:" translate="no">​</a></h3>
<ul>
<li class="">Pandas: little nice intro to <a href="https://pandas.pydata.org/" target="_blank" rel="noopener noreferrer" class="">pandas</a>,
nothing that I don't already know, but it was nice to work in a small group on the same task.
I have never done that since college.</li>
<li class="">Decorators: also the basics about writing decorators. It was well organized, and the tutor was open to all questions which were really varying from entry to advanced python experience levels. This gives a very nice example on how one should do a tutorial. Well done!</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="second-day">Second Day<a href="https://ammar-najjar.com/blog/pyconde-pydata-2019#second-day" class="hash-link" aria-label="Direct link to Second Day" title="Direct link to Second Day" translate="no">​</a></h2>
<p>I noticed that there are many cancelled talks this day. Too bad for people applied and didn't get accepted due to amount of applications.
Well they mentioned the reasons behind that the very next morning, and it was mainly due to last minute illness (flu) or hardware breakdown.</p>
<p>I had a very interesting talk with <a href="https://github.com/ambv" target="_blank" rel="noopener noreferrer" class="">Łukasz Langa</a>, the original author of <a href="https://github.com/psf/black" target="_blank" rel="noopener noreferrer" class="">Black</a>, and he handed me over some black stickers in person.</p>
<p>Also <a href="https://github.com/hynek" target="_blank" rel="noopener noreferrer" class="">Hynek Schlawack</a> was there, and got to give him my feedback related to <a href="https://github.com/python-attrs/attrs" target="_blank" rel="noopener noreferrer" class="">attrs</a>.</p>
<p>I attended:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="talks-1">Talks:<a href="https://ammar-najjar.com/blog/pyconde-pydata-2019#talks-1" class="hash-link" aria-label="Direct link to Talks:" title="Direct link to Talks:" translate="no">​</a></h3>
<ul>
<li class="">Keynote: Python 2020+: very interesting points on how the release manager of CPython 3.8 thinks the development direction should be.</li>
<li class="">Docker 101: These were two docker introductory talks, If I knew what was in there, I would not have attended. They coverd the very basics of docker.</li>
<li class="">Python panel: alwys very interesting to talk to the core developer, and see how things are seen from the other side of the beloved CPython interpreter development cycle. They encourage and inspire people to contribute to the project.</li>
<li class="">Static Typing in Python: nothing really new here, but nice to encourage more people to use type hints.</li>
<li class="">The GIL: every now and then the GIL limitation problem rises, and this talk was a very nice investication process by the speaker on how they inspected their multi-threded python code and found out the origin of the problem lying in the GIL.</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="tutorials-1">Tutorials:<a href="https://ammar-najjar.com/blog/pyconde-pydata-2019#tutorials-1" class="hash-link" aria-label="Direct link to Tutorials:" title="Direct link to Tutorials:" translate="no">​</a></h3>
<ul>
<li class="">Introduction to parallelism and concurrency: nice, but the tutor made it to the third slide only 15 min before the end of the session.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="third-day">Third Day<a href="https://ammar-najjar.com/blog/pyconde-pydata-2019#third-day" class="hash-link" aria-label="Direct link to Third Day" title="Direct link to Third Day" translate="no">​</a></h2>
<p>I attended:</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="talks-2">Talks:<a href="https://ammar-najjar.com/blog/pyconde-pydata-2019#talks-2" class="hash-link" aria-label="Direct link to Talks:" title="Direct link to Talks:" translate="no">​</a></h3>
<ul>
<li class="">Keynote: Rethinking open source: very interesting, I might refer to it when arguing about beinging open source into the interprise someday.</li>
<li class="">Pytest: a very basic introduction.</li>
<li class="">Pytest: a very instructive talk to show how to try and mimic pytest itself, interesting talk!</li>
<li class="">What's new in Python 3.8: self explainatory.</li>
<li class="">Your name is invalid: talks about the difficulties people face when interacting with forign systems when it comes to unicode, insightful.</li>
<li class="">Python vs R: a nice basic comparision, actually I only attended it for the lack of an interesting talk at that time slot.</li>
<li class="">Data visualization: nice intro to two visualization libraries in python.</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="sprint-days">Sprint Days<a href="https://ammar-najjar.com/blog/pyconde-pydata-2019#sprint-days" class="hash-link" aria-label="Direct link to Sprint Days" title="Direct link to Sprint Days" translate="no">​</a></h2>
<p>At the night before I checked some of the suggested sprint topics, such as <a href="https://github.com/scikit-learn/scikit-learn" target="_blank" rel="noopener noreferrer" class="">scikit-learn</a> and <a href="https://github.com/bokeh/bokeh" target="_blank" rel="noopener noreferrer" class="">Bokeh</a>, so I cloned the repos and looked at the open issues.</p>
<p>Sprints started at about 10:00.</p>
<p>At first I met <a href="https://github.com/scoder" target="_blank" rel="noopener noreferrer" class="">Stefan Behnel</a>, the <a href="https://github.com/cython/cython" target="_blank" rel="noopener noreferrer" class="">cython</a> maintainer, and after a small talk I was curious to see stuff inside cython, then I remembered the famous saying for cython developers: "We write C so you don't have to". So as I am in noway a C fan, I wanted to look into some other stuff.</p>
<p>So I participated in the Bokeh sprint, and for two days, opened two issues, and submitted one <a href="https://github.com/bokeh/bokeh/pull/9276" target="_blank" rel="noopener noreferrer" class="">PR</a> which got merged fast. We started during the sprint with adding static-typing for the python part, but I didn't make a real deal, so I didn't submit a PR for that change, and just consider it as a training! I learned a lot talking to <a href="https://github.com/mattpap" target="_blank" rel="noopener noreferrer" class="">Mateusz Paprocki</a> about Bokeh and its infrastructure.</p>
<p>That's how Bokeh sticker made its way to the back of my laptop, in addition to Black!</p>
<!-- -->
<img src="https://ammar-najjar.com/assets/images/bokeh_logo-cb8fd56fc352e8e9fb6ec08fcdb6a9e2.png" alt="bokeh-logo" width="100" height="120">
<img src="https://ammar-najjar.com/assets/images/black_logo-0ea040c0c2a8b3ec235bf1248d48f58f.png" alt="bokeh-logo" width="200" height="100">
<a href="https://ammar-najjar.com/blog/pyconde-pydata-2019#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Python</category>
        </item>
        <item>
            <title><![CDATA[Book Review: The Pragmatic Programmer, 20th Anniversary Edition]]></title>
            <link>https://ammar-najjar.com/blog/pragmatic-programmer</link>
            <guid>https://ammar-najjar.com/blog/pragmatic-programmer</guid>
            <pubDate>Mon, 23 Sep 2019 22:44:23 GMT</pubDate>
            <description><![CDATA[Book review of The Pragmatic Programmer (20th Anniversary Edition) by David Thomas and Andrew Hunt — timeless advice on software craftsmanship, career growth, and writing maintainable code.]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="about-the-book">About the book:<a href="https://ammar-najjar.com/blog/pragmatic-programmer#about-the-book" class="hash-link" aria-label="Direct link to About the book:" title="Direct link to About the book:" translate="no">​</a></h2>
<ul>
<li class="">Author: David Thomas, Andrew Hunt</li>
<li class="">ISBN: 978-0-1359-5705-9</li>
<li class="">Official website: <a href="https://pragprog.com/book/tpp20/the-pragmatic-programmer-20th-anniversary-edition" target="_blank" rel="noopener noreferrer" class="">https://pragprog.com/book/tpp20/the-pragmatic-programmer-20th-anniversary-edition</a></li>
</ul>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="summery">Summery:<a href="https://ammar-najjar.com/blog/pragmatic-programmer#summery" class="hash-link" aria-label="Direct link to Summery:" title="Direct link to Summery:" translate="no">​</a></h2>
<p>This book gives a definition for being a pragmatic programmer, then talks about some of the basic principles to being one. It take a theoretical approach, with examples in the form of stories, to explain a specific problem, and how should one approach it, and solve it.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="highlights">Highlights:<a href="https://ammar-najjar.com/blog/pragmatic-programmer#highlights" class="hash-link" aria-label="Direct link to Highlights:" title="Direct link to Highlights:" translate="no">​</a></h3>
<p>What I take with me from it:</p>
<ul>
<li class="">
<p>1-3: Software Entropy:</p>
<blockquote>
<p>Tip 3: Don't live with broken windows</p>
</blockquote>
</li>
<li class="">
<p>1-7: Communicate:</p>
<blockquote>
<p>Tip 13: Build documentation in, don't bolt it on</p>
</blockquote>
</li>
<li class="">
<p>6-36: Blackboards:</p>
<blockquote>
<p>Tip 60: Use blackboards to coordinate workflow</p>
</blockquote>
</li>
<li class="">
<p>7-42: Property-Based Testing:</p>
<blockquote>
<p>Tip 71: Use property-based tests to validate your assumptions</p>
</blockquote>
</li>
</ul>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="opinion">Opinion<a href="https://ammar-najjar.com/blog/pragmatic-programmer#opinion" class="hash-link" aria-label="Direct link to Opinion" title="Direct link to Opinion" translate="no">​</a></h2>
<p>For someone, like me, who already has experience with more than one programming language, prioritise testing, and familiar with the agile development methods, this book looks some kind of repetition or an emphasise for the information one already know.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion:<a href="https://ammar-najjar.com/blog/pragmatic-programmer#conclusion" class="hash-link" aria-label="Direct link to Conclusion:" title="Direct link to Conclusion:" translate="no">​</a></h2>
<ul>
<li class="">I recommend it if you don't appreciate testing, or early refactoring, or want to know more about agile development methods, ways to better architect your projects. It is a good read after all.</li>
<li class="">Level: Beginner to Intermediate</li>
</ul>
<a href="https://ammar-najjar.com/blog/pragmatic-programmer#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Book Review</category>
        </item>
        <item>
            <title><![CDATA[Book Review: Introduction to Python for Science and Engineering]]></title>
            <link>https://ammar-najjar.com/blog/intro-python-science-and-engineering</link>
            <guid>https://ammar-najjar.com/blog/intro-python-science-and-engineering</guid>
            <pubDate>Wed, 11 Sep 2019 20:19:25 GMT</pubDate>
            <description><![CDATA[Book review of Introduction to Python for Science and Engineering by David J. Pine — a concise, practical introduction to Python for scientists and engineers using NumPy, SciPy, and Matplotlib.]]></description>
            <content:encoded><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="about-the-book">About the book:<a href="https://ammar-najjar.com/blog/intro-python-science-and-engineering#about-the-book" class="hash-link" aria-label="Direct link to About the book:" title="Direct link to About the book:" translate="no">​</a></h3>
<ul>
<li class="">Author: David J. Pine</li>
<li class="">ISBN: 978-1-138-58389-4 (Paperback)</li>
<li class="">ISBN: 978-1-138-58390-0 (Hardback)</li>
</ul>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="summery">Summery:<a href="https://ammar-najjar.com/blog/intro-python-science-and-engineering#summery" class="hash-link" aria-label="Direct link to Summery:" title="Direct link to Summery:" translate="no">​</a></h3>
<p>This book gives an introduction to python as a language, then dives into some engineering problems and how they can be solved using python:</p>
<ul>
<li class="">Plotting (matplotlib)</li>
<li class="">Curve Fitting (linear regression and nonlinear least squares)</li>
<li class="">Numerical Routines (SciPy and Numpy)</li>
<li class="">Data manipulation (Pandas)</li>
<li class="">Animation (matplotlib)</li>
<li class="">Intro to Python GUI (PyQt)</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="opinion">Opinion<a href="https://ammar-najjar.com/blog/intro-python-science-and-engineering#opinion" class="hash-link" aria-label="Direct link to Opinion" title="Direct link to Opinion" translate="no">​</a></h3>
<p>I skipped the first few chapters and read only the chapters which introduce numpy, matplotlib, scipy and pandas.
I found it nice to read, and had a jupyter notebook open on the side, to try some examples. The examples were clear and easy to understand. I enjoyed reading it.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion:<a href="https://ammar-najjar.com/blog/intro-python-science-and-engineering#conclusion" class="hash-link" aria-label="Direct link to Conclusion:" title="Direct link to Conclusion:" translate="no">​</a></h3>
<ul>
<li class="">Level: Intermediate</li>
<li class="">I recommend it for students of information engineering, and who finished their degree.</li>
</ul>
<a href="https://ammar-najjar.com/blog/intro-python-science-and-engineering#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Python</category>
            <category>Book Review</category>
        </item>
        <item>
            <title><![CDATA[Book Review: Python Descriptors]]></title>
            <link>https://ammar-najjar.com/blog/python-descriptors</link>
            <guid>https://ammar-najjar.com/blog/python-descriptors</guid>
            <pubDate>Tue, 03 Sep 2019 21:03:49 GMT</pubDate>
            <description><![CDATA[Book review of Python Descriptors by Jacob Zimmerman — how Python's descriptor protocol works, when to use it, and practical examples for building reusable attribute logic.]]></description>
            <content:encoded><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="about-the-book">About the book:<a href="https://ammar-najjar.com/blog/python-descriptors#about-the-book" class="hash-link" aria-label="Direct link to About the book:" title="Direct link to About the book:" translate="no">​</a></h3>
<ul>
<li class="">Author: Jacob Zimmerman</li>
<li class="">ISBN (pbk): 978-1-4842-3726-7</li>
<li class="">ISBN (electronic): 978-1-4842-3727-4</li>
<li class="">URL: <a href="https://link.springer.com/book/10.1007%2F978-1-4842-3727-4" target="_blank" rel="noopener noreferrer" class="">https://link.springer.com/book/10.1007%2F978-1-4842-3727-4</a></li>
</ul>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="summery">Summery:<a href="https://ammar-najjar.com/blog/python-descriptors#summery" class="hash-link" aria-label="Direct link to Summery:" title="Direct link to Summery:" translate="no">​</a></h3>
<p>This book has two parts:</p>
<ul>
<li class="">
<p>First on explaining what descriptors are, data vs non-data ones, and basic implementation of the built in <code>classmethod</code>, <code>staticmethod</code> and <code>property</code> descriptors.</p>
</li>
<li class="">
<p>Second on showing how to implement your custom descriptors.</p>
</li>
</ul>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="highlights">Highlights:<a href="https://ammar-najjar.com/blog/python-descriptors#highlights" class="hash-link" aria-label="Direct link to Highlights:" title="Direct link to Highlights:" translate="no">​</a></h3>
<p>Chapter 4: Descriptors in the Standard Library</p>
<ul>
<li class="">The property Class</li>
</ul>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">property</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> fget</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> fset</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> fdel</span><span class="token operator" style="color:#393A34">=</span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fget </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> fget</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fset </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> fset</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fdel </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> fdel</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__get__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> instance</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> owner</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> instance </span><span class="token keyword" style="color:#00009f">is</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">elif</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fget </span><span class="token keyword" style="color:#00009f">is</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">raise</span><span class="token plain"> AttributeError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"unreadable attribute"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">else</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fget</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">instance</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__set__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> instance</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> value</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fset </span><span class="token keyword" style="color:#00009f">is</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">raise</span><span class="token plain"> AttributeError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"can't set attribute"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">else</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fset</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">instance</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> value</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__delete__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> instance</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">if</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fdel </span><span class="token keyword" style="color:#00009f">is</span><span class="token plain"> </span><span class="token boolean" style="color:#36acaa">None</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            </span><span class="token keyword" style="color:#00009f">raise</span><span class="token plain"> AttributeError</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">"can't delete attribute"</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">else</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fdel</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">instance</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">getter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> fget</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">type</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">fget</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fset</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fdel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">setter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> fset</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">type</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fget</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> fset</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fdel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">deleter</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> fdel</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> </span><span class="token builtin">type</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fget</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">fset</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> fdel</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<ul>
<li class="">The classmethod descriptor</li>
</ul>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">classmethod</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> func</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">func </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> func</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__get__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> instance</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> owner</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> functools</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">partial</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">func</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> owner</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<ul>
<li class="">The staticmethod descriptor</li>
</ul>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">staticmethod</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> func</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">func </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> func</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__get__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> instance</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> owner</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">func</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="opinion">Opinion<a href="https://ammar-najjar.com/blog/python-descriptors#opinion" class="hash-link" aria-label="Direct link to Opinion" title="Direct link to Opinion" translate="no">​</a></h3>
<p>I enjoyed reading this book, especially the first part, it shows a view port of a developer who is not involved in the core development of python itself. He sometimes also says, ask a core developer if you wanna get the exact reasoning behind it 😄</p>
<p>You don't need an editor open deside you while reading, so I pretty much read it on the bus.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion:<a href="https://ammar-najjar.com/blog/python-descriptors#conclusion" class="hash-link" aria-label="Direct link to Conclusion:" title="Direct link to Conclusion:" translate="no">​</a></h3>
<ul>
<li class="">Level: Advanced</li>
<li class="">I recommend this book for python developer who like to know what is going under the hood, twinkle with the interiors functionality of python, and curious how every thing works.</li>
</ul>
<a href="https://ammar-najjar.com/blog/python-descriptors#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Python</category>
            <category>Book Review</category>
        </item>
        <item>
            <title><![CDATA[Book Review: Python Tricks, The Book]]></title>
            <link>https://ammar-najjar.com/blog/python-tricks</link>
            <guid>https://ammar-najjar.com/blog/python-tricks</guid>
            <pubDate>Sun, 01 Sep 2019 23:20:14 GMT</pubDate>
            <description><![CDATA[Book review of Python Tricks by Dan Bader — a practical guide to writing more Pythonic code with clean patterns, idiomatic techniques, and hidden language features.]]></description>
            <content:encoded><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="about-the-book">About the book:<a href="https://ammar-najjar.com/blog/python-tricks#about-the-book" class="hash-link" aria-label="Direct link to About the book:" title="Direct link to About the book:" translate="no">​</a></h3>
<ul>
<li class="">Author: Dan Bader</li>
<li class="">ISBN: 9781775093305 (paperback)</li>
<li class="">ISBN: 9781775093312 (electronic)</li>
</ul>
<!-- -->
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="summery">Summery:<a href="https://ammar-najjar.com/blog/python-tricks#summery" class="hash-link" aria-label="Direct link to Summery:" title="Direct link to Summery:" translate="no">​</a></h3>
<p>This book gives some basic tips for python beginners, so like a flower from each field. Actually not each field, but the very basic very well known fields: assert, dunders, args &amp; kwargs, data structures, loops.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="highlights">Highlights:<a href="https://ammar-najjar.com/blog/python-tricks#highlights" class="hash-link" aria-label="Direct link to Highlights:" title="Direct link to Highlights:" translate="no">​</a></h3>
<blockquote>
<p>7.4 The Craziest Dict Expression in the West</p>
</blockquote>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token operator" style="color:#393A34">&gt;&gt;</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'yes'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'no'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">1.0</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'maybe'</span><span class="token punctuation" style="color:#393A34">}</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token punctuation" style="color:#393A34">{</span><span class="token boolean" style="color:#36acaa">True</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">'maybe'</span><span class="token punctuation" style="color:#393A34">}</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="opinion">Opinion<a href="https://ammar-najjar.com/blog/python-tricks#opinion" class="hash-link" aria-label="Direct link to Opinion" title="Direct link to Opinion" translate="no">​</a></h3>
<p>I found myself just flipping through pages rather than reading the book, all seemed very basic and shallow. Don't get me wrong, it is well organizsed and formatted, but targeted for people who just started doing python, maybe followed the first tutorial, or had their first week playing with the language.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion:<a href="https://ammar-najjar.com/blog/python-tricks#conclusion" class="hash-link" aria-label="Direct link to Conclusion:" title="Direct link to Conclusion:" translate="no">​</a></h3>
<ul>
<li class="">Level: Entry</li>
<li class="">I would never recommend it for anyone who had even one month doing python.</li>
</ul>
<a href="https://ammar-najjar.com/blog/python-tricks#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Python</category>
            <category>Book Review</category>
        </item>
        <item>
            <title><![CDATA[Fetch using Gitlab API]]></title>
            <link>https://ammar-najjar.com/blog/fetch-with-gitlab-api</link>
            <guid>https://ammar-najjar.com/blog/fetch-with-gitlab-api</guid>
            <pubDate>Tue, 19 Feb 2019 15:34:35 GMT</pubDate>
            <description><![CDATA[How to use the GitLab API with Python to fetch repository data, automate git operations, and integrate GitLab into your development workflow.]]></description>
            <content:encoded><![CDATA[<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="motivation">Motivation<a href="https://ammar-najjar.com/blog/fetch-with-gitlab-api#motivation" class="hash-link" aria-label="Direct link to Motivation" title="Direct link to Motivation" translate="no">​</a></h2>
<p>I use <a href="https://about.gitlab.com/" target="_blank" rel="noopener noreferrer" class="">gitlab</a>, and I have many repos that I need to track on daily basis, or even more than once per day. I found it so tyring first of all to clone all those repos when I change my working machine, and then to keep all these repos up to date. For that reason I began to write an automation script to do the boring tasks of my shoulders.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="process">Process<a href="https://ammar-najjar.com/blog/fetch-with-gitlab-api#process" class="hash-link" aria-label="Direct link to Process" title="Direct link to Process" translate="no">​</a></h2>
<p>I started with a simple bash script, which go through a for-loop and do the stuff, where the paths of the repos must be hard-coded.</p>
<iframe width="100%" frameborder="0" id="gist-37c649e0ed2289187e330ed351b42f68-main.sh"></iframe>
<p>This was fast and worked for a while, but when I got to work with multiple projects, and my co-workers added/removed/renamed a repo, it was sill manual work to update the script, and as boring as I am, I did not like it, and looked for a lazy way to automate the process, and opened the Gitlab <a href="https://docs.gitlab.com/ee/api/" target="_blank" rel="noopener noreferrer" class="">API-Documentation</a>, where the fun started.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="solution-idea">Solution Idea<a href="https://ammar-najjar.com/blog/fetch-with-gitlab-api#solution-idea" class="hash-link" aria-label="Direct link to Solution Idea" title="Direct link to Solution Idea" translate="no">​</a></h2>
<p>A python script to talk to the gitlab API, and use the <a href="https://docs.gitlab.com/ee/api/#project-resources" target="_blank" rel="noopener noreferrer" class=""><code>/projects</code></a> end point to access the repos.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="requirements">Requirements:<a href="https://ammar-najjar.com/blog/fetch-with-gitlab-api#requirements" class="hash-link" aria-label="Direct link to Requirements:" title="Direct link to Requirements:" translate="no">​</a></h3>
<ul>
<li class="">Gtilab <a href="https://docs.gitlab.com/ee/user/profile/personal_access_tokens.html#personal-access-tokens" target="_blank" rel="noopener noreferrer" class="">Token</a>: create a private token first to use it accessing the API as explained in the <a href="https://docs.gitlab.com/ee/api/#personal-access-tokens" target="_blank" rel="noopener noreferrer" class="">docs</a>.</li>
<li class="">Virtual access to the repos (e.g.: gitlab.com)</li>
</ul>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="optional">Optional:<a href="https://ammar-najjar.com/blog/fetch-with-gitlab-api#optional" class="hash-link" aria-label="Direct link to Optional:" title="Direct link to Optional:" translate="no">​</a></h4>
<ul>
<li class=""><a href="https://github.com/pypa/pipenv" target="_blank" rel="noopener noreferrer" class="">Pipenv</a> to install the dev-dependencies:</li>
</ul>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">brew install pipenv</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="first-try-argparse">First Try: <a href="https://docs.python.org/3/library/argparse.html" target="_blank" rel="noopener noreferrer" class="">argparse</a><a href="https://ammar-najjar.com/blog/fetch-with-gitlab-api#first-try-argparse" class="hash-link" aria-label="Direct link to first-try-argparse" title="Direct link to first-try-argparse" translate="no">​</a></h3>
<p>The first try was to pass the options as arguments to the script:</p>
<iframe width="100%" frameborder="0" id="gist-37c649e0ed2289187e330ed351b42f68-main-args.py"></iframe>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="usage">Usage:<a href="https://ammar-najjar.com/blog/fetch-with-gitlab-api#usage" class="hash-link" aria-label="Direct link to Usage:" title="Direct link to Usage:" translate="no">​</a></h4>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">usage: main.py [-h] [--token TOKEN] [--url URL]</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">optional arguments:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  -h, --help            show this help message and exit</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  --token TOKEN, -t TOKEN</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                        Gitlab private Token</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  --url URL, -u URL     Gitlab URL&lt;Paste&gt;</span><br></div></code></pre></div></div>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="second-try-json">Second Try: <a href="https://docs.python.org/3/library/json.html?highlight=json#module-json" target="_blank" rel="noopener noreferrer" class="">JSON</a><a href="https://ammar-najjar.com/blog/fetch-with-gitlab-api#second-try-json" class="hash-link" aria-label="Direct link to second-try-json" title="Direct link to second-try-json" translate="no">​</a></h3>
<p>Then I thought that the configurations must be more flexible, so the use of a separate config file make sense.</p>
<p>Full project repository can be found <a href="https://github.com/ammarnajjar/fetch-from-gitlab" target="_blank" rel="noopener noreferrer" class="">here</a></p>
<iframe width="100%" frameborder="0" id="gist-37c649e0ed2289187e330ed351b42f68-main.py"></iframe>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="usage-1">Usage<a href="https://ammar-najjar.com/blog/fetch-with-gitlab-api#usage-1" class="hash-link" aria-label="Direct link to Usage" title="Direct link to Usage" translate="no">​</a></h4>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">python main.py</span><br></div></code></pre></div></div>
<a href="https://ammar-najjar.com/blog/fetch-with-gitlab-api#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Python</category>
            <category>Git</category>
            <category>Gitlab</category>
        </item>
        <item>
            <title><![CDATA[RxJS Series - Observables]]></title>
            <link>https://ammar-najjar.com/blog/rxjs-observables</link>
            <guid>https://ammar-najjar.com/blog/rxjs-observables</guid>
            <pubDate>Mon, 18 Feb 2019 00:00:00 GMT</pubDate>
            <description><![CDATA[An introduction to RxJS Observables — what they are, how they differ from Promises, and how to use them for reactive programming in JavaScript and Angular.]]></description>
            <content:encoded><![CDATA[<p>RxJS (<a href="https://github.com/ReactiveX/rxjs" target="_blank" rel="noopener noreferrer" class="">on Github</a>) is a reactive programming library for Javascript, and in this series, I would like to expose my way of learning it with you.</p>
<!-- -->
<p>From <a href="https://en.wikipedia.org/wiki/Reactive_programming" target="_blank" rel="noopener noreferrer" class="">Wikipedia</a>:</p>
<blockquote>
<p>In computing, reactive programming is a declarative programming paradigm concerned with data streams and the propagation of change. With this paradigm it is possible to express static (e.g., arrays) or dynamic (e.g., event emitters) data streams with ease, and also communicate that an inferred dependency within the associated execution model exists, which facilitates the automatic propagation of the changed data flow.</p>
</blockquote>
<p>From <a href="https://angular.io/guide/rx-library" target="_blank" rel="noopener noreferrer" class="">Angular Docs</a>:</p>
<blockquote>
<p>Reactive programming is an asynchronous programming paradigm concerned with data streams and the propagation of change. RxJS (Reactive Extensions for JavaScript) is a library for reactive programming using observables that makes it easier to compose asynchronous or callback-based code.</p>
</blockquote>
<p>I will take first the basic building blocks then the most commonly used operators.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="building-blocks">Building Blocks<a href="https://ammar-najjar.com/blog/rxjs-observables#building-blocks" class="hash-link" aria-label="Direct link to Building Blocks" title="Direct link to Building Blocks" translate="no">​</a></h2>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="observable--observer--subscriber">Observable &amp; Observer &amp; Subscriber<a href="https://ammar-najjar.com/blog/rxjs-observables#observable--observer--subscriber" class="hash-link" aria-label="Direct link to Observable &amp; Observer &amp; Subscriber" title="Direct link to Observable &amp; Observer &amp; Subscriber" translate="no">​</a></h3>
<p>The concept of observers and observables are connected to each other, for there is no functioning observable without an observer.</p>
<p>Let's check the docs first:</p>
<p>Observer is (from <a href="http://reactivex.io/rxjs/class/es6/MiscJSDoc.js~ObserverDoc.html" target="_blank" rel="noopener noreferrer" class="">Docs</a>):</p>
<blockquote>
<p>An interface for a consumer of push-based notifications delivered by an Observable.</p>
</blockquote>
<p>Subscriber is (from <a href="http://reactivex.io/rxjs/class/es6/Subscriber.js~Subscriber.html%3CPaste%3E" target="_blank" rel="noopener noreferrer" class="">Docs</a>):</p>
<blockquote>
<p>Implements the Observer interface and extends the Subscription class. While the Observer is the public API for consuming the values of an Observable, all Observers get converted to a Subscriber, in order to provide Subscription-like capabilities such as unsubscribe.</p>
</blockquote>
<p>Observable is (from <a href="http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html" target="_blank" rel="noopener noreferrer" class="">Docs</a>):</p>
<blockquote>
<p>A representation of any set of values over any amount of time. This is the most basic building block of RxJS.</p>
</blockquote>
<p>So the subscriber is an observer, and all observers gets converted to subscribers to support the subscribe/unsubscribe functionality.</p>
<a href="https://ammar-najjar.com/blog/rxjs-observables#" class="go-to-top">Go to Top</a>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="from-scratch">From Scratch<a href="https://ammar-najjar.com/blog/rxjs-observables#from-scratch" class="hash-link" aria-label="Direct link to From Scratch" title="Direct link to From Scratch" translate="no">​</a></h4>
<p>The source code of an Observer look like this:</p>
<iframe width="100%" frameborder="0" id="gist-74e1a9696b2b685052573c1aabcf31a9-observer.ts"></iframe>
<p>It implements three methods:</p>
<ul>
<li class=""><code>next</code>: a callback to receive notifications of type <code>next</code></li>
<li class=""><code>error</code>: a callback to receive notifications of type <code>error</code></li>
<li class=""><code>complete</code>: a callback to receive notifications of type <code>complete</code></li>
</ul>
<p>The observable can just push notifications to the observer on every occasion it desires, i.e one could implement a function that does that:</p>
<iframe width="100%" frameborder="0" id="gist-74e1a9696b2b685052573c1aabcf31a9-customObservable1.ts"></iframe>
<p>So if we call our <code>customObservable</code> with an observer as a parameter:</p>
<iframe width="100%" frameborder="0" id="gist-74e1a9696b2b685052573c1aabcf31a9-call_customObservable1.ts"></iframe>
<p>The output will look like:</p>
<iframe width="100%" frameborder="0" id="gist-74e1a9696b2b685052573c1aabcf31a9-out1.sh "></iframe>
<p>This is a basic form of observable, but if it was that simple, why to need a whole framework around it? The answer is simple, try to call <code>next</code> after <code>complete</code>:</p>
<iframe width="100%" frameborder="0" id="gist-74e1a9696b2b685052573c1aabcf31a9-customObservable2.ts"></iframe>
<p>The output will look like:</p>
<iframe width="100%" frameborder="0" id="gist-74e1a9696b2b685052573c1aabcf31a9-out2.sh"></iframe>
<p>This does not really work the way wanted.</p>
<p>(<a href="https://stackblitz.com/edit/rxjs-01" target="_blank" rel="noopener noreferrer" class="">Try on StackBlitz</a>)</p>
<p>Let's take a more complex example, where the observable wraps a stream of numbers over interval:</p>
<iframe width="100%" frameborder="0" id="gist-74e1a9696b2b685052573c1aabcf31a9-customObservable3.ts"></iframe>
<p>The output will appear one value every second, like:</p>
<iframe width="100%" frameborder="0" id="gist-74e1a9696b2b685052573c1aabcf31a9-out3.sh"></iframe>
<p>(<a href="https://stackblitz.com/edit/rxjs-02" target="_blank" rel="noopener noreferrer" class="">Try on StackBlitz</a>)</p>
<p>So an observable is basically a wrapper over stream or set of values, it presents it in the way required. A stream is a sequence of ongoing events ordered in time.</p>
<p>The relation between an observer and an observable is shown in the following animation:</p>
<p><img decoding="async" loading="lazy" alt="Animation" src="https://ammar-najjar.com/assets/images/observer-animation-60de2df4b7e0123dd76fd4b43a42a718.gif" width="600" height="368" class="img_ev3q"></p>
<p>The observable can emit either a value, an error or a complete notification signal, which indicate that the stream is over.
The observer captures these emitted events by defining functions to react on each event. The way an observer listens with it to an observable is called "<strong>Subscription</strong>"</p>
<p>So by defining an observer, the stream is defined, and by subscribing to it, actions for the emitted events are defined. So logically, for every observable, a subscription is needed to create actions, and that will be clear in the examples following.</p>
<p>As long as you have more than one set or stream of values, you can imagine how many operations one might need to apply on them.
RxJS provide many ways to create an observable with all the safety and asynchronous features that one would desire.</p>
<h4 class="anchor anchorTargetStickyNavbar_Vzrq" id="create-observable-using-rxjs-static-methods">Create Observable using RxJS Static Methods<a href="https://ammar-najjar.com/blog/rxjs-observables#create-observable-using-rxjs-static-methods" class="hash-link" aria-label="Direct link to Create Observable using RxJS Static Methods" title="Direct link to Create Observable using RxJS Static Methods" translate="no">​</a></h4>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="--of">- <a href="http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#static-method-of" target="_blank" rel="noopener noreferrer" class=""><code>of</code></a>:<a href="https://ammar-najjar.com/blog/rxjs-observables#--of" class="hash-link" aria-label="Direct link to --of" title="Direct link to --of" translate="no">​</a></h5>
<p>By wrapping a sequence of values with <code>of</code> returns an observable with those values where it emits the values in the sequence as given, then completes.</p>
<iframe width="100%" frameborder="0" id="gist-74e1a9696b2b685052573c1aabcf31a9-rxjs-of.ts"></iframe>
<p><a href="https://ammar-najjar.com/blog/rxjs-observables#" class="go-to-top">Go to Top</a>
(<a href="https://stackblitz.com/edit/rxjs-03" target="_blank" rel="noopener noreferrer" class="">Try on StackBlitz</a>)</p>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="--range">- <a href="http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#static-method-range" target="_blank" rel="noopener noreferrer" class=""><code>range</code></a>:<a href="https://ammar-najjar.com/blog/rxjs-observables#--range" class="hash-link" aria-label="Direct link to --range" title="Direct link to --range" translate="no">​</a></h5>
<p>This reminds me with python's <code>range</code> function, it emits a sequence of numbers determined with the range parameters <code>(start, count)</code>.</p>
<iframe width="100%" frameborder="0" id="gist-74e1a9696b2b685052573c1aabcf31a9-rxjs-range.ts"></iframe>
<p><a href="https://ammar-najjar.com/blog/rxjs-observables#" class="go-to-top">Go to Top</a>
(<a href="https://stackblitz.com/edit/rxjs-05" target="_blank" rel="noopener noreferrer" class="">Try on StackBlitz</a>)</p>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="--interval">- <a href="http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#static-method-interval" target="_blank" rel="noopener noreferrer" class=""><code>interval</code></a>:<a href="https://ammar-najjar.com/blog/rxjs-observables#--interval" class="hash-link" aria-label="Direct link to --interval" title="Direct link to --interval" translate="no">​</a></h5>
<p>This takes a parameter as time in milliseconds and generates a sequence of numbers every time interval. That way one gets infinite time series of numbers.</p>
<iframe width="100%" frameborder="0" id="gist-74e1a9696b2b685052573c1aabcf31a9-rxjs-interval.ts"></iframe>
<p><a href="https://ammar-najjar.com/blog/rxjs-observables#" class="go-to-top">Go to Top</a>
(<a href="https://stackblitz.com/edit/rxjs-06" target="_blank" rel="noopener noreferrer" class="">Try on StackBlitz</a>)</p>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="--timer">- <a href="http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#static-method-timer" target="_blank" rel="noopener noreferrer" class=""><code>timer</code></a>:<a href="https://ammar-najjar.com/blog/rxjs-observables#--timer" class="hash-link" aria-label="Direct link to --timer" title="Direct link to --timer" translate="no">​</a></h5>
<p>This takes two parameters <code>(initialDelay, period)</code>. The initial delay could be a numbers representing time in milliseconds, or a <a href="https://developer.mozilla.org/en-US/docs/Web/JavaScript/Reference/Global_Objects/Date" target="_blank" rel="noopener noreferrer" class="">date</a> object. The observable waits for the initial delay value, then it starts emitting numbers starting with <code>0</code> every period of time.</p>
<iframe width="100%" frameborder="0" id="gist-74e1a9696b2b685052573c1aabcf31a9-rxjs-timer.ts"></iframe>
<p><a href="https://ammar-najjar.com/blog/rxjs-observables#" class="go-to-top">Go to Top</a>
(<a href="https://stackblitz.com/edit/rxjs-07" target="_blank" rel="noopener noreferrer" class="">Try on StackBlitz</a>)</p>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="--from">- <a href="http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#static-method-from" target="_blank" rel="noopener noreferrer" class=""><code>from</code></a>:<a href="https://ammar-najjar.com/blog/rxjs-observables#--from" class="hash-link" aria-label="Direct link to --from" title="Direct link to --from" translate="no">​</a></h5>
<p>This is a magical method, which converts so many data types into observables: Array, Array-like, Promise, iterable object, string (as an array of chars).</p>
<iframe width="100%" frameborder="0" id="gist-74e1a9696b2b685052573c1aabcf31a9-rxjs-from.ts"></iframe>
<p><a href="https://ammar-najjar.com/blog/rxjs-observables#" class="go-to-top">Go to Top</a>
(<a href="https://stackblitz.com/edit/rxjs-04" target="_blank" rel="noopener noreferrer" class="">Try on StackBlitz</a>) &gt;&gt; <em><code>take</code> operator is used here to show the first 5 values of the infinite stream, it will be discussed later in detail</em></p>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="--fromevent">- <a href="http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#static-method-fromEvent" target="_blank" rel="noopener noreferrer" class=""><code>fromEvent</code></a>:<a href="https://ammar-najjar.com/blog/rxjs-observables#--fromevent" class="hash-link" aria-label="Direct link to --fromevent" title="Direct link to --fromevent" translate="no">​</a></h5>
<p>This method is useful wile working with the DOM, for it creates observable from <a href="https://developer.mozilla.org/en-US/docs/Web/API/Document_Object_Model/Introduction" target="_blank" rel="noopener noreferrer" class="">DOM</a> events or Node.js <a href="https://nodejs.org/api/events.html" target="_blank" rel="noopener noreferrer" class="">EventEmitter</a> events.
It takes two parameters <code>(element: EventTarget, eventName: string)</code></p>
<iframe width="100%" frameborder="0" id="gist-74e1a9696b2b685052573c1aabcf31a9-rxjs-fromEvent.ts"></iframe>
<p><a href="https://ammar-najjar.com/blog/rxjs-observables#" class="go-to-top">Go to Top</a>
(<a href="https://stackblitz.com/edit/rxjs-08" target="_blank" rel="noopener noreferrer" class="">Try on StackBlitz</a>)</p>
<h5 class="anchor anchorTargetStickyNavbar_Vzrq" id="--create">- <a href="http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html#static-method-create" target="_blank" rel="noopener noreferrer" class=""><code>create</code></a>:<a href="https://ammar-najjar.com/blog/rxjs-observables#--create" class="hash-link" aria-label="Direct link to --create" title="Direct link to --create" translate="no">​</a></h5>
<p>This creates a custom observable which will execute the specified function when an observer subscribes to it, it also can return a function which will be executed when the observer unsubscribes. This is the most flexible way of creating an observable.</p>
<p>In the following example a counter is set to count once every second, and this counter is an observable which return the function which will be executed when the observer unsubscribes.</p>
<iframe width="100%" frameborder="0" id="gist-74e1a9696b2b685052573c1aabcf31a9-rxjs-create.ts"></iframe>
<p><a href="https://ammar-najjar.com/blog/rxjs-observables#" class="go-to-top">Go to Top</a>
(<a href="https://stackblitz.com/edit/rxjs-10" target="_blank" rel="noopener noreferrer" class="">Try on StackBlitz</a>)</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="conclusion">Conclusion<a href="https://ammar-najjar.com/blog/rxjs-observables#conclusion" class="hash-link" aria-label="Direct link to Conclusion" title="Direct link to Conclusion" translate="no">​</a></h2>
<p>By now, the most common ways of creating an observable in RxJS should be clear, and I can move to the next step in the building blocks of RxJS, which are the most used operators on observables.</p>
<p>Until next blog post!</p>
<hr>
<p>More References to Observables:</p>
<ul>
<li class=""><a href="http://reactivex.io/documentation/observable.html" target="_blank" rel="noopener noreferrer" class="">ReactiveX Docs</a></li>
<li class=""><a href="http://reactivex.io/rxjs/class/es6/Observable.js~Observable.html" target="_blank" rel="noopener noreferrer" class="">ReactiveX API</a></li>
</ul>
<a href="https://ammar-najjar.com/blog/rxjs-observables#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Reactive</category>
        </item>
        <item>
            <title><![CDATA[Custom Selenium Webdrivers using Decorators]]></title>
            <link>https://ammar-najjar.com/blog/custom-selenium-webdrivers</link>
            <guid>https://ammar-najjar.com/blog/custom-selenium-webdrivers</guid>
            <pubDate>Sun, 09 Apr 2017 07:33:18 GMT</pubDate>
            <description><![CDATA[How to dynamically create custom Selenium WebDriver classes with a parametrized browser binary path using Python decorators.]]></description>
            <content:encoded><![CDATA[<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-main-question">The main question:<a href="https://ammar-najjar.com/blog/custom-selenium-webdrivers#the-main-question" class="hash-link" aria-label="Direct link to The main question:" title="Direct link to The main question:" translate="no">​</a></h3>
<p>How to create custom selenium webdriver classes dynamically with a parametrized browser binary path?</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-motivation">The motivation:<a href="https://ammar-najjar.com/blog/custom-selenium-webdrivers#the-motivation" class="hash-link" aria-label="Direct link to The motivation:" title="Direct link to The motivation:" translate="no">​</a></h3>
<p>Working on website functional testing using selenium webdriver, generates the urge to run tests using different versions of the most commonly used browsers these days, in parallel on the same machine.</p>
<!-- -->
<p>Tests can use <a href="https://pypi.python.org/pypi/parameterized/0.6.1" target="_blank" rel="noopener noreferrer" class="">parametrized</a> where the parameter can be the driver class itself, and here comes the need to generate drivers classes dynamically and pass them as parameters to the tests.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-idea">The idea:<a href="https://ammar-najjar.com/blog/custom-selenium-webdrivers#the-idea" class="hash-link" aria-label="Direct link to The idea:" title="Direct link to The idea:" translate="no">​</a></h3>
<p>In order to be able to use more than one version of a browser, first of all one needs to install these versions locally, then create a custom webdriver class generator with a custom binary path and use these generated classes in the tests wihtout being initialized.</p>
<h3 class="anchor anchorTargetStickyNavbar_Vzrq" id="the-procedure">The procedure:<a href="https://ammar-najjar.com/blog/custom-selenium-webdrivers#the-procedure" class="hash-link" aria-label="Direct link to The procedure:" title="Direct link to The procedure:" translate="no">​</a></h3>
<p>I am going to explain the steps I followed regarding the most pobular browsers these days: <a href="https://www.mozilla.org/en-US/firefox/products/" target="_blank" rel="noopener noreferrer" class="">Mozilla Firefox</a> and <a href="https://www.google.com/chrome/" target="_blank" rel="noopener noreferrer" class="">Google Chrome</a>.</p>
<ul>
<li class="">
<p>Download Firefox versions you want to use in testing from <a href="https://ftp.mozilla.org/pub/firefox/releases/" target="_blank" rel="noopener noreferrer" class="">Mozilla releases archive</a>.</p>
</li>
<li class="">
<p>For Chrome it might take some effort, by the instructions are clear on the <a href="https://www.chromium.org/getting-involved/download-chromium" target="_blank" rel="noopener noreferrer" class="">Chromium project site</a>, I am going to list them here just in case they got missing:</p>
<ul>
<li class="">Look in <a href="http://googlechromereleases.blogspot.com/search/label/Stable%20updates" target="_blank" rel="noopener noreferrer" class="">googlechromereleases.blogspot.com</a> for the last time the wanted version was mentioned.</li>
<li class="">Loop up that version history (e.g.: "44.0.2403.157") in the Position Lookup</li>
<li class="">In this case it returns a base position of "330231". This is the commit of where the 44 release was branched, back in May 2015.*</li>
<li class="">Open the continuous builds archive</li>
<li class="">Click through on your platform (Linux/Mac/Win)</li>
<li class="">Paste "330231" into the filter field at the top and wait for all the results to XHR in.</li>
<li class="">Eventually I get a perfect hit: <a href="https://commondatastorage.googleapis.com/chromium-browser-snapshots/index.html?" target="_blank" rel="noopener noreferrer" class="">commondatastorage.googleapis.com/chromium-browser-snapshots</a></li>
<li class="">Sometimes you may have to decrement the commit number until you find one.</li>
<li class="">Download and run!</li>
</ul>
</li>
<li class="">
<p>Now you have all your versions ready and able to run, there are many options where you can save them where they can be accessable system wide. One option would be to save them in an environment variables.</p>
</li>
<li class="">
<p>Write a custom webdriver class generator, where the binary path is a parameter and can be dynamically changed in selenium tests. But the first problem I faced here was to be able to change the <code>__init__</code> method for our custom class and pass the custom binary path to it. I found out that it can be done by deriving from the class <code>type</code>:</p>
</li>
</ul>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">binary </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> FirefoxBinary</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">bin_path</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> firefox_binary</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">binary</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    webdriver</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Firefox</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> firefox_binary</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">firefox_binary</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Firefox </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">type</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'Firefox'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">webdriver</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Firefox</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">'__init__'</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> __init__</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<ul>
<li class="">For google chrome, changing the binary path is done a little bit differently:</li>
</ul>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">chrome_options </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Options</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">chrome_options</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">binary_location </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> bin_path</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> chrome_options</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">chrome_options</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    webdriver</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Chrome</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> chrome_options</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">chrome_options</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Chrome </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">type</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'Chrome'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">webdriver</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Chrome</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">'__init__'</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> __init__</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<ul>
<li class="">It would be more practical to put these functionality into decorators. For this purpose I wrote two <a href="https://wiki.python.org/moin/PythonDecorators" target="_blank" rel="noopener noreferrer" class="">decorators</a> one to generate Firefox webdriver objects:</li>
</ul>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">CustomFirefox</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> bin_path</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">bin_path </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> bin_path</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__call__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cls</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        binary </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> FirefoxBinary</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">bin_path</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> firefox_binary</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">binary</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            webdriver</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Firefox</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> firefox_binary</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">firefox_binary</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Firefox </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">type</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'Firefox'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">webdriver</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Firefox</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">'__init__'</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> __init__</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Firefox</span><br></div></code></pre></div></div>
<p>and the other for Chrome webdriver objects:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">CustomChrome</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> bin_path</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">bin_path </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> bin_path</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__call__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> cls</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        chrome_options </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> Options</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        chrome_options</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">binary_location </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> self</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">bin_path</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">def</span><span class="token plain"> </span><span class="token function" style="color:#d73a49">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> chrome_options</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">chrome_options</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            webdriver</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Chrome</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">__init__</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">self</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> chrome_options</span><span class="token operator" style="color:#393A34">=</span><span class="token plain">chrome_options</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        Chrome </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">type</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'Chrome'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">webdriver</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Chrome</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">{</span><span class="token string" style="color:#e3116c">'__init__'</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> __init__</span><span class="token punctuation" style="color:#393A34">}</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        </span><span class="token keyword" style="color:#00009f">return</span><span class="token plain"> Chrome</span><br></div></code></pre></div></div>
<ul>
<li class="">Now all what one needs to do is to cast the appropriate decorator onto a class, pass the path to the custom binary. For example:</li>
</ul>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token decorator annotation punctuation" style="color:#393A34">@CustomFirefox</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'&lt;path_to_firefox_binary&gt;'</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token keyword" style="color:#00009f">class</span><span class="token plain"> </span><span class="token class-name">Driver</span><span class="token punctuation" style="color:#393A34">:</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">pass</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    </span><span class="token keyword" style="color:#00009f">print</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Driver</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>The output would look like:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">&lt;class '__main__.Firefox'&gt;</span><br></div></code></pre></div></div>
<p>To open the browser, just call the newly created webdriver object:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">Driver</span><span class="token punctuation" style="color:#393A34">(</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<ul>
<li class="">This works fine, except that the original meta-data from webdriver class are gone, so in order to keep the meta-data from the parent class <code>webdriver.Firefox</code> or <code>webdriver.Chrom</code> we need to save that data first before changeing the <code>__init__</code> method, and then restore them afterwards, for example for Firefox decorator:</li>
</ul>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">meta_data </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">dict</span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">webdriver</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Firefox</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">__dict__</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">meta_data</span><span class="token punctuation" style="color:#393A34">[</span><span class="token string" style="color:#e3116c">'__init__'</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> __init__</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Firefox </span><span class="token operator" style="color:#393A34">=</span><span class="token plain"> </span><span class="token builtin">type</span><span class="token punctuation" style="color:#393A34">(</span><span class="token string" style="color:#e3116c">'Firefox'</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">webdriver</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain">Firefox</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> meta_data</span><span class="token punctuation" style="color:#393A34">)</span><br></div></code></pre></div></div>
<p>Now whe we check the returned class, we get:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">&lt;class 'selenium.webdriver.firefox.webdriver.WebDriver'&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">``¨</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">- A little tweek might be helpful to include the default case where no path is passed as a parameter, so that the decorator generate the default webdriver with the default browser version installed on the system.</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">- Now it is possible to create a drivers factory to generate custom webdrivers from a list of binary paths, where the parameters to that factory are the decorator and the list of binary paths:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">```python</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">def driver_factory(decorator, bin_paths):</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    drivers = []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    for bin_path in bin_paths:</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        @decorator(bin_path)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        class CustomDriver: pass</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        drivers.append(CustomDriver)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">return drivers</span><br></div></code></pre></div></div>
<ul>
<li class="">
<p>Voala! The webdriver is ready to be used in tests!</p>
</li>
<li class="">
<p>The complete script would look like:</p>
</li>
</ul>
<iframe width="100%" frameborder="0" id="gist-827aa1638377b265148daad20dbda7d8"></iframe>
<p>It might be possible to do the same functionality using a meta class. I will look into that later.
If you have any comments or suggestions please share it with me.</p>
<a href="https://ammar-najjar.com/blog/custom-selenium-webdrivers#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Python</category>
            <category>Selenium</category>
        </item>
        <item>
            <title><![CDATA[Fedora23 Upgrade (unlabeled home)]]></title>
            <link>https://ammar-najjar.com/blog/fedora23-upgrade</link>
            <guid>https://ammar-najjar.com/blog/fedora23-upgrade</guid>
            <pubDate>Sun, 03 Jul 2016 17:07:02 GMT</pubDate>
            <description><![CDATA[How to fix the unlabeled home directory issue encountered when upgrading from Fedora 23 to Fedora 24, with the exact SELinux relabeling steps.]]></description>
            <content:encoded><![CDATA[<p>When I tried to update from the fedora23 to fedora24, I faced the issue that the <code>home</code> directoy lost its labeling:</p>
<!-- -->
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">&gt; ls -lZ /home/</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">drwxr-x---. 83 username username system_u:object_r:unlabeled_t:s0   4096 Jun 22 14:55 username</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">drwx------.  2 root     root     system_u:object_r:lost_found_t:s0  16384 Sep 30 2015 lost+found</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">drwx------.  5 root     root     system_u:object_r:unlabeled_t:s0   4096 Jan 13 11:48 testuser</span><br></div></code></pre></div></div>
<p>And the issue prevents the user <code>username</code> from logging in to its home directory, instead it directs the user to the root directory with an error message regarding permission issues.</p>
<p>I searched for the solution and found it here in this <a href="http://forums.fedoraforum.org/showpost.php?p=1587276&amp;postcount=2" target="_blank" rel="noopener noreferrer" class="">helpful answer</a>.</p>
<p>The command to re-label the directory is:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sudo restorecon -R /home</span><br></div></code></pre></div></div>
<p>After that everythig went ok and logging is back to normal:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">&gt; ls -lZ /home/</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">drwxr-x---. 83 username username system_u:object_r:user_home_dir_t:s0  4096 Jun 22 14:55 username</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">drwx------.  2 root     root     system_u:object_r:lost_found_t:s0     16384 Sep 30 2015 lost+found</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">drwx------.  5 root     root     system_u:object_r:user_home_dir_t:s0  4096 Jan 13 11:48 testuser</span><br></div></code></pre></div></div>
<a href="https://ammar-najjar.com/blog/fedora23-upgrade#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Fedora</category>
        </item>
        <item>
            <title><![CDATA[Build Vim from Source]]></title>
            <link>https://ammar-najjar.com/blog/build-vim-from-source</link>
            <guid>https://ammar-najjar.com/blog/build-vim-from-source</guid>
            <pubDate>Thu, 23 Jun 2016 18:22:02 GMT</pubDate>
            <description><![CDATA[Step-by-step guide to compiling Vim from source on Linux to enable Python support, client-server mode, and other features missing from distribution packages.]]></description>
            <content:encoded><![CDATA[<p>Many times when I want to use vim which comes within the main repo of any Linux distro, I find that it is missing some kind of support, either python or client-server mode, or any other feature. And there for I have to build it manually from source and install it.</p>
<!-- -->
<p>In this post, I show the steps to do that on fedora, as it is my current distro. I had to collect the steps from many resources including <a href="http://vimdoc.sourceforge.net/htmldoc/" target="_blank" rel="noopener noreferrer" class="">vimdoc</a>, <a href="http://vim.wikia.com/wiki/Building_Vim" target="_blank" rel="noopener noreferrer" class="">vim-wiki</a> and many stackoverflow answers and blogs. So I wanted to blog these steps to come back to them anytime I need to build vim from source with:</p>
<ul>
<li class="">Python3 support.</li>
<li class="">Ruby support.</li>
<li class="">Lua support.</li>
<li class="">Perl support.</li>
</ul>
<script src="https://gist.github.com/ammarnajjar/dd612a063194adea8699667f0c9161e1.js"></script>
<p>In case python-2.7 is needed instead of python3, just replace them in the config lines:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">	--enable-pythoninterp                               \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">	--with-python-config-dir=/usr/lib/python2.7/config  \</span><br></div></code></pre></div></div>
<a href="https://ammar-najjar.com/blog/build-vim-from-source#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Vim</category>
        </item>
        <item>
            <title><![CDATA[Lwa4p Robot Arm Setup]]></title>
            <link>https://ammar-najjar.com/blog/lwa4p-robot</link>
            <guid>https://ammar-najjar.com/blog/lwa4p-robot</guid>
            <pubDate>Wed, 22 Jun 2016 14:43:32 GMT</pubDate>
            <description><![CDATA[How to set up the Schunk Powerball LWA4P robot arm with MoveIt and ROS — the steps, pitfalls, and a complete install script from real lab experiments.]]></description>
            <content:encoded><![CDATA[<p>During my experiments with the powerball lightweight <a href="http://mobile.schunk-microsite.com/en/produkte/products/powerball-lightweight-arm-lwa-4p.html" target="_blank" rel="noopener noreferrer" class="">lwa4p robot arm</a> from Schunk, I faced many difficulties in order to make it move using <a href="http://moveit.ros.org/" target="_blank" rel="noopener noreferrer" class="">moveit</a>. In this post I share this experience.</p>
<p>I collected all the steps needed in one <a href="https://github.com/ammarnajjar/lwa4p_robot_arm/blob/master/install.sh" target="_blank" rel="noopener noreferrer" class="">bash script</a>, and here I am going to go into the details of the process that I followed.</p>
<!-- -->
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="choosing-can-carddriver">Choosing CAN Card/Driver<a href="https://ammar-najjar.com/blog/lwa4p-robot#choosing-can-carddriver" class="hash-link" aria-label="Direct link to Choosing CAN Card/Driver" title="Direct link to Choosing CAN Card/Driver" translate="no">​</a></h2>
<p>As lwa4p robot arm uses CAN-bus communication protocol, a CAN card is needed to connect to the arm.
In another <a href="http://www.cs.rpi.edu/foswiki/bin/view/RoboticsWeb/PowerballSchunk" target="_blank" rel="noopener noreferrer" class="">project</a>, Bryant Pong used Peak pcan card from <a href="http://www.peak-system.com/" target="_blank" rel="noopener noreferrer" class="">Peak</a> which is not available to me, and according to sockercan interface <a href="http://wiki.ros.org/socketcan_interface#Tested_Drivers_and_Devices" target="_blank" rel="noopener noreferrer" class="">wiki-page</a> I could also use the <a href="https://esd.eu/en" target="_blank" rel="noopener noreferrer" class="">esd</a> CAN cards with the Kernel driver, and that is what I used.</p>
<a href="https://ammar-najjar.com/blog/lwa4p-robot#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="choosing-software-version">Choosing Software Version<a href="https://ammar-najjar.com/blog/lwa4p-robot#choosing-software-version" class="hash-link" aria-label="Direct link to Choosing Software Version" title="Direct link to Choosing Software Version" translate="no">​</a></h2>
<p>First off, is to choose the operating system that I was going to work on. The other <a href="http://www.cs.rpi.edu/foswiki/bin/view/RoboticsWeb/PowerballSchunk" target="_blank" rel="noopener noreferrer" class="">project</a> uses <a href="http://wiki.ros.org/hydro" target="_blank" rel="noopener noreferrer" class="">ros-hydro</a>, which supports only Precise, Quantal, and Raring for debian packages as stated in the <a href="http://wiki.ros.org/hydro/Installation/Ubuntu#hydro.2BAC8-Installation.2BAC8-Sources.Setup_your_sources.list" target="_blank" rel="noopener noreferrer" class="">wiki-page</a>. So the first idea was to do similar to the mentioned project. Of course dealing with old software draws a lot of troubles, such as the need to install some packages from source as they are not supported in the main software repositories, and one would have to deal with dependencies.</p>
<p>Long story short, it did not work. So I contacted one of the authors of <a href="https://github.com/ipa320/ipa_canopen" target="_blank" rel="noopener noreferrer" class="">ipa320/ipa_canopen</a>, Mr. Mathias Luedtke <a href="https://github.com/ipa-mdl" target="_blank" rel="noopener noreferrer" class="">@ipa-mdl</a>, who kindly gave me some useful guidelines.</p>
<p>It was suggested to use <a href="https://github.com/ros-industrial/ros_canopen" target="_blank" rel="noopener noreferrer" class="">ros-industrial/ros_canopen</a> for it supports <a href="http://wiki.ros.org/indigo" target="_blank" rel="noopener noreferrer" class="">ros-indigo</a> on <a href="http://releases.ubuntu.com/14.04/" target="_blank" rel="noopener noreferrer" class="">Ubuntu-14.04</a>.
So I ended up with:</p>
<ul>
<li class="">Ubuntu-14.04-64bit</li>
<li class="">ROS-Indigo (the most recent update)</li>
<li class="">Socketcan (ESD CAN card (PLX90xx), sja1000 kernel driver)</li>
<li class=""><a href="https://www.gnu.org/software/bash/" target="_blank" rel="noopener noreferrer" class="">Bash</a> shell.</li>
</ul>
<a href="https://ammar-najjar.com/blog/lwa4p-robot#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="installing-ros-packages">Installing ROS Packages<a href="https://ammar-najjar.com/blog/lwa4p-robot#installing-ros-packages" class="hash-link" aria-label="Direct link to Installing ROS Packages" title="Direct link to Installing ROS Packages" translate="no">​</a></h2>
<p>To install <a href="http://www.ros.org/" target="_blank" rel="noopener noreferrer" class="">ROS</a>, I followed the instructions for the installation mentioned in the <a href="http://wiki.ros.org/action/fullsearch/indigo/Installation/Ubuntu" target="_blank" rel="noopener noreferrer" class="">wiki-page</a>.</p>
<ul>
<li class="">As an administrator or a sudo user, add ROS repositories to the sourcelist and import the appropriate key:</li>
</ul>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sudo sh -c 'echo "deb http://packages.ros.org/ros/ubuntu $(lsb_release -sc) main" &gt; /etc/apt/sources.list.d/ros-latest.list'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sudo apt-key adv --keyserver hkp://ha.pool.sks-keyservers.net --recv-key 0xB01FA116</span><br></div></code></pre></div></div>
<ul>
<li class="">Update the cache for packages on the system:</li>
</ul>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sudo apt-get update</span><br></div></code></pre></div></div>
<ul>
<li class="">Install ROS desktop:</li>
</ul>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sudo apt-get install -y ros-indigo-desktop</span><br></div></code></pre></div></div>
<ul>
<li class="">Install other complementary packages:</li>
</ul>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sudo apt-get install -y git ros-indigo-libntcan ros-indigo-libpcan       \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        ros-indigo-controller-manager ros-indigo-controller-manager-msgs \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        ros-indigo-joint-limits-interface ros-indigo-cob-srvs            \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        ros-indigo-cob-control-mode-adapter ros-indigo-cob-dashboard     \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        ros-indigo-cob-command-gui libmuparser-dev python-rosinstall     \</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        python-wstool</span><br></div></code></pre></div></div>
<a href="https://ammar-najjar.com/blog/lwa4p-robot#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="rosdep-initialization">rosdep Initialization<a href="https://ammar-najjar.com/blog/lwa4p-robot#rosdep-initialization" class="hash-link" aria-label="Direct link to rosdep Initialization" title="Direct link to rosdep Initialization" translate="no">​</a></h2>
<p><code>rosdep</code> enables you to easily install system dependencies for source you want to compile and is required to run some core components in ROS.</p>
<p>To initialize it:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sudo rosdep init</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">rosdep update</span><br></div></code></pre></div></div>
<p>Note that the second command should be run as a normal user.</p>
<a href="https://ammar-najjar.com/blog/lwa4p-robot#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="environment-setup">Environment Setup<a href="https://ammar-najjar.com/blog/lwa4p-robot#environment-setup" class="hash-link" aria-label="Direct link to Environment Setup" title="Direct link to Environment Setup" translate="no">​</a></h2>
<p>In order to run any ROS command, ROS environment variables should be available, and this can be done by sourcing specific files:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">echo "source /opt/ros/indigo/setup.bash" &gt;&gt; ~/.bashrc</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">source ~/.bashrc</span><br></div></code></pre></div></div>
<p>Be careful to have that setup file sourced on every terminal you use for ROS, and if you are using <a href="https://www.gnu.org/software/screen/" target="_blank" rel="noopener noreferrer" class="">screen</a> or <a href="https://tmux.github.io/" target="_blank" rel="noopener noreferrer" class="">tmux</a>, make sure that in every pane you open, every setup file is sourced, else you might get un-expected errors.</p>
<a href="https://ammar-najjar.com/blog/lwa4p-robot#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="catkin-workspace">Catkin Workspace<a href="https://ammar-najjar.com/blog/lwa4p-robot#catkin-workspace" class="hash-link" aria-label="Direct link to Catkin Workspace" title="Direct link to Catkin Workspace" translate="no">​</a></h2>
<ul>
<li class="">Create a directory for catkin workspace. Usually I just call it <code>catkin_ws</code> and place it in my home directory:</li>
</ul>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">mkdir -p $HOME/catkin_ws/src</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">cd $HOME/catkin_ws/src</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">catkin_init_workspace</span><br></div></code></pre></div></div>
<ul>
<li class="">Clone the needed packages from github source:</li>
</ul>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git clone https://github.com/ammarnajjar/ros_canopen.git -b no-lost-arbitration-handling</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">git clone https://github.com/ipa320/schunk_robots.git -b indigo_dev</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">cd ..</span><br></div></code></pre></div></div>
<p>Notice that I forked the original <a href="https://github.com/ros-industrial/ros_canopen" target="_blank" rel="noopener noreferrer" class="">ros-industrial/ros_canopen</a> and applied a <a href="https://gist.github.com/ammarnajjar/99c84d31216600d97ce28879e4ac3580" target="_blank" rel="noopener noreferrer" class="">patch</a> to it, as I kept getting an error:<code>internal_error: 2 (lost arbitration;)</code> when I try to initialize the robot arm. If you are curious, have a look at the <a href="https://gist.github.com/ammarnajjar/32a3082d75df68a565de1c3b3504ebe2" target="_blank" rel="noopener noreferrer" class="">complete logs</a> for the mentioned error.<br>
<!-- -->The patch is displayed here to show where I commented out handling that error. Do this only if you have to and as a last resort.</p>
<script src="https://gist.github.com/ammarnajjar/99c84d31216600d97ce28879e4ac3580.js"></script>
<ul>
<li class="">Install the dependencies for those packages using:</li>
</ul>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">rosdep install --from-paths src --ignore-src --rosdistro indigo -y</span><br></div></code></pre></div></div>
<ul>
<li class="">Build catkin workspace:</li>
</ul>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">catkin_make</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">echo "source $HOME/catkin_ws/devel/setup.bash" &gt;&gt; $HOME/.bashrc</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">source $HOME/catkin_ws/devel/setup.bash</span><br></div></code></pre></div></div>
<a href="https://ammar-najjar.com/blog/lwa4p-robot#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="can-interface">CAN Interface<a href="https://ammar-najjar.com/blog/lwa4p-robot#can-interface" class="hash-link" aria-label="Direct link to CAN Interface" title="Direct link to CAN Interface" translate="no">​</a></h2>
<p>Setting up the CAN interface correctly came after some research in <a href="http://wiki.ros.org/socketcan_interface" target="_blank" rel="noopener noreferrer" class="">ros/socket_interface</a> and with some instructions mentioned in ipa320/schunk_robots opened <a href="https://github.com/ipa320/schunk_robots/issues/59" target="_blank" rel="noopener noreferrer" class="">Bug #59</a>.</p>
<p>I connect the robot arm to <code>can0</code> so the steps can be summarized in the following:</p>
<ul>
<li class="">Set the bitrate to 500k. This has to be done while the interface is down, so first turn it down, set the bitrate value, then turn it up again:</li>
</ul>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sudo ip link set dev can0 down</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sudo ip link set can0 type can bitrate 500000</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">sudo ip link set dev can0 up</span><br></div></code></pre></div></div>
<ul>
<li class="">Set the txqueue length to be between (15-20):</li>
</ul>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">sudo ifconfig can0 txqueuelen 20</span><br></div></code></pre></div></div>
<p>Notice that <code>schunk_robots</code> package uses <code>can0</code> by default, so if you use a different interface such as <code>can1</code> or <code>can2</code>, make sure to modify the configuration file <code>lwa4p.yml</code> in your <code>schunk_robots</code> package to make it use the matching CAN interface.</p>
<a href="https://ammar-najjar.com/blog/lwa4p-robot#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="moveit">Moveit<a href="https://ammar-najjar.com/blog/lwa4p-robot#moveit" class="hash-link" aria-label="Direct link to Moveit" title="Direct link to Moveit" translate="no">​</a></h2>
<p>To use <a href="http://moveit.ros.org/" target="_blank" rel="noopener noreferrer" class="">moveit</a>, I followed the steps mentioned in the <a href="http://moveit.ros.org/install/" target="_blank" rel="noopener noreferrer" class="">docs</a>.
I chose the catkin workspace to be in my home directory:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">mkdir -p $HOME/moveit/src</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">cd $HOME/moveit/src</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">wstool init .</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">wstool merge https://raw.github.com/ros-planning/moveit_docs/indigo-devel/moveit.rosinstall</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">wstool update</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">cd ..</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">rosdep install --from-paths src --ignore-src --rosdistro indigo -y</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">catkin_make</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">echo "source $HOME/moveit/devel/setup.bash" &gt;&gt; $HOME/.bashrc</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">source $HOME/moveit/devel/setup.bash</span><br></div></code></pre></div></div>
<p>At the time when I was using moveit, I faced some compilation <a href="https://gist.github.com/ammarnajjar/6e53d340fc4b6a0afab4de93d46c70d0" target="_blank" rel="noopener noreferrer" class="">errors</a>, but I figured out the problem and made a <a href="https://github.com/ros-planning/moveit_ros/pull/680" target="_blank" rel="noopener noreferrer" class="">pull request</a> with a suggested solution, and it was merged in no time to ros-planning/moveit-ros <a href="https://github.com/ros-planning/moveit_ros/tree/indigo-devel" target="_blank" rel="noopener noreferrer" class="">indigo-devel branch</a>, and the issue is solved.</p>
<p>After the installation is complete, I used <a href="http://docs.ros.org/hydro/api/moveit_setup_assistant/html/doc/tutorial.html" target="_blank" rel="noopener noreferrer" class="">MoveIt Setup Assistant</a> to generate the moveit configurations. I did it already myself and put it on <a href="https://github.com/ammarnajjar/lwa4p_moveit_config" target="_blank" rel="noopener noreferrer" class="">github</a>, with all its tweaks.</p>
<p>If you don't want to do it yourself, go to your <code>src</code> directory in <code>catkin</code> workspace and clone it:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">git clone https://github.com/ammarnajjar/lwa4p_moveit_config.git</span><br></div></code></pre></div></div>
<p>Then re-build again to be able to use it.</p>
<a href="https://ammar-najjar.com/blog/lwa4p-robot#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="initializing-the-robot-arm">Initializing the Robot Arm<a href="https://ammar-najjar.com/blog/lwa4p-robot#initializing-the-robot-arm" class="hash-link" aria-label="Direct link to Initializing the Robot Arm" title="Direct link to Initializing the Robot Arm" translate="no">​</a></h2>
<p>To initialize the robot arm, I launch the controller first</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">roslaunch schunk_lwa4p robot.launch</span><br></div></code></pre></div></div>
<p>then call the <code>init</code> service:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">rosservice call /arm/driver/init</span><br></div></code></pre></div></div>
<p>This should give the following output:</p>
<div class="language-log codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-log codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">success: True</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">message: ''</span><br></div></code></pre></div></div>
<a href="https://ammar-najjar.com/blog/lwa4p-robot#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="moving-the-robot-arm">Moving the Robot Arm<a href="https://ammar-najjar.com/blog/lwa4p-robot#moving-the-robot-arm" class="hash-link" aria-label="Direct link to Moving the Robot Arm" title="Direct link to Moving the Robot Arm" translate="no">​</a></h2>
<p>Launch <code>rviz</code> and use it to control the robot arm:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">roslaunch lwa4p_movit_config moveit_planning_execution.launch</span><br></div></code></pre></div></div>
<p>The following images show the robot arm in two different positions:</p>
<!-- -->
<img src="https://ammar-najjar.com/assets/images/lwa4p-home-482f2c1bec6006c799ebc79cf288896e.jpg" alt="lwa4p-home" height="700">
<p><em>home position</em></p>
<img src="https://ammar-najjar.com/assets/images/lwa4p-random-valid-309f93792cc9ccb9632c2049d1dd6678.jpg" alt="lwa4p-home" width="500">
<p><em>random valid position</em></p>
<a href="https://ammar-najjar.com/blog/lwa4p-robot#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Robotics</category>
        </item>
        <item>
            <title><![CDATA[Adventure through my vimrc (nvimrc)]]></title>
            <link>https://ammar-najjar.com/blog/vimrc-adventure</link>
            <guid>https://ammar-najjar.com/blog/vimrc-adventure</guid>
            <pubDate>Sun, 19 Jun 2016 08:38:02 GMT</pubDate>
            <description><![CDATA[A guided tour through a Vim and Neovim configuration — plugins, key mappings, and settings accumulated over years of daily use, with explanations for each choice.]]></description>
            <content:encoded><![CDATA[<p>In this post I'm going to take you in an adventure in my previously <code>vimrc</code> and currently <a href="https://github.com/ammarnajjar/dotfiles/blob/master/init.vim" target="_blank" rel="noopener noreferrer" class="">init.vim</a> which I collected from using <a href="http://www.vim.org/" target="_blank" rel="noopener noreferrer" class="">vim</a> then <a href="https://neovim.io/" target="_blank" rel="noopener noreferrer" class="">neovim</a> along the last few years. I will explain the feature or the function, then include the configurations or code that should be inserted in the <code>vimrc</code> (<code>init.vim</code>) file to activate that feature.</p>
<!-- -->
<p>This might not always be up to date, for I keep changing these configurations when I feel like it.</p>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="general-configurations-neovim">General Configurations (neovim)<a href="https://ammar-najjar.com/blog/vimrc-adventure#general-configurations-neovim" class="hash-link" aria-label="Direct link to General Configurations (neovim)" title="Direct link to General Configurations (neovim)" translate="no">​</a></h2>
<p>The very first section is for the general settings which I think that they should be set up in vim by default, as some of them already are default in <a href="https://neovim.io/" target="_blank" rel="noopener noreferrer" class="">neovim</a>, which include:</p>
<ul>
<li class="">Use incremental search: this feature enables vim from highlighting the search pattern while it is being typed.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set incsearch</span><br></div></code></pre></div></div>
<ul>
<li class="">Use fast terminal connection, this feature improves smoothness of redrawing when there are multiple windows.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set ttyfast</span><br></div></code></pre></div></div>
<ul>
<li class="">When a file has been detected to have been changed outside of vim and it has not been changed inside of vim, automatically read it again.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set autoread</span><br></div></code></pre></div></div>
<ul>
<li class="">Activate the wild menu for command line completion using the <code>TAB</code> key.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set wildmenu</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set wildmode=longest:list,full</span><br></div></code></pre></div></div>
<ul>
<li class="">Highlight all matches for the search pattern.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set hlsearch</span><br></div></code></pre></div></div>
<ul>
<li class="">Vim uses history to remember command line commands that a user enters in either on of these command-lines:<!-- -->
<ul>
<li class=""><code>:</code> commands</li>
<li class="">search strings</li>
<li class="">expressions</li>
<li class="">input lines, typed for the <code>input()</code> function.</li>
<li class="">debug mode commands
I like to make vim remember a lot (1000) of my command history.</li>
</ul>
</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set history=1000</span><br></div></code></pre></div></div>
<ul>
<li class="">Make vim behave in a more useful way (this literally exactly what is written in <a href="http://vimdoc.sourceforge.net/htmldoc/options.html#%27compatible%27" target="_blank" rel="noopener noreferrer" class="">vim docs</a>). It is optional to include this in one's vimrc, for when a vimrc or gvimrc file is found while Vim is starting up,	this option is enabled.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set nocompatible</span><br></div></code></pre></div></div>
<ul>
<li class="">Make backspace erase character as other editors do.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set backspace=2</span><br></div></code></pre></div></div>
<ul>
<li class="">When a tab character is inserted using pressing the <code>&lt;TAB&gt;</code> key, backspace is used to delete what was inserted, it depends on other configurations parameters <code>shiftwidth</code>.  <code>tabstop</code> and <code>softtabstop</code>.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set smarttab</span><br></div></code></pre></div></div>
<ul>
<li class="">When starting a new line, use the same indentation from previous line. This is a very useful feature when one is coding, especially in an indent sensitive language such as python or yaml.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set autoindent</span><br></div></code></pre></div></div>
<ul>
<li class="">Live substitution (neovim only), when replacing a pattern.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set inccommand=nosplit</span><br></div></code></pre></div></div>
<a href="https://ammar-najjar.com/blog/vimrc-adventure#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="general-configurations-vim">General Configurations (vim)<a href="https://ammar-najjar.com/blog/vimrc-adventure#general-configurations-vim" class="hash-link" aria-label="Direct link to General Configurations (vim)" title="Direct link to General Configurations (vim)" translate="no">​</a></h2>
<p>Another set of general configurations which are not included in <a href="https://neovim.io/" target="_blank" rel="noopener noreferrer" class="">neovim</a> by default. I still find them very useful.</p>
<ul>
<li class="">I don't like to use the mouse while I'm editing, still I like to enable it.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set mouse=a</span><br></div></code></pre></div></div>
<ul>
<li class="">Show (partial) command in the last line of the screen. The <a href="http://vimdoc.sourceforge.net/htmldoc/options.html#%27showcmd%27" target="_blank" rel="noopener noreferrer" class="">docs</a> explain this very well so I copied that from there:
In Visual mode the size of the selected area is shown:<!-- -->
<ul>
<li class="">When selecting characters within a line, the number of characters. If the number of bytes is different it is also displayed: "2-6" means two characters and six bytes.</li>
<li class="">When selecting more than one line, the number of lines.</li>
<li class="">When selecting a block, the size in screen characters: <code>{lines}x{columns}</code>.</li>
</ul>
</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set showcmd</span><br></div></code></pre></div></div>
<ul>
<li class="">Show the matching of a bracket when inserted, this looks like as if the cursor moved to the matching one for a small period of time to highlight it. <code>matchtime</code> is to control that time period.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set showmatch</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set matchtime=2</span><br></div></code></pre></div></div>
<ul>
<li class="">Use case insensitive when searching for a match.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set ignorecase</span><br></div></code></pre></div></div>
<ul>
<li class="">If the search pattern contains upper case characters, override the <code>ignorecase</code> option and use case sensitive search.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set smartcase</span><br></div></code></pre></div></div>
<ul>
<li class="">Automatically save before executing commands like <code>:next</code> and <code>:make</code>. I do use <a href="https://www.gnu.org/software/make/manual/make.html" target="_blank" rel="noopener noreferrer" class="">make</a> a lot, so this is very efficient.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set autowrite</span><br></div></code></pre></div></div>
<ul>
<li class="">Hide buffers when they are abandoned, if they do not show in other windows of course. This is useful when I deal with many buffers at a time, and I don't have to worry about saving before switching.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set hidden</span><br></div></code></pre></div></div>
<ul>
<li class="">I find modeline very useful, when I get other's code, or when I want to give others my code, I set specific settings inside the modeline to be set automatically for any other user who usees vim, the most important ones are <code>tabstop</code> and <code>expandtab</code>.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">setglobal modeline</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set modelines=3</span><br></div></code></pre></div></div>
<ul>
<li class="">I like to have a minimal number of screen lines to keep above and below the cursor.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set scrolloff=3</span><br></div></code></pre></div></div>
<ul>
<li class="">Show the mode that I am using as a message on the last line, weather it is <code>insert</code>, <code>visual</code> or <code>replace</code> mode.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set showmode</span><br></div></code></pre></div></div>
<ul>
<li class="">Wrap text and show long lines on multiple lines without inserting line breaks.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set wrap</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set linebreak</span><br></div></code></pre></div></div>
<ul>
<li class="">Set the title of the window to the value of 'titlestring', and if empty to: filename [+==] path - vim-server-name.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set title</span><br></div></code></pre></div></div>
<ul>
<li class="">Show line number on the left side of the document on the active line, and activate relative numbers on the rest of the lines.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set number</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set relativenumber</span><br></div></code></pre></div></div>
<ul>
<li class="">set the path to the shell to use 'bash'</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set shell=/bin/bash</span><br></div></code></pre></div></div>
<ul>
<li class="">Ignore temporary and complied files for c++, Java and Python as well as the configurations folders for the version control systems that I might use.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set wildignore=*.o,*~,*.pyc,*.class</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set wildignore+=*/.git/*,*/.hg/*,*/.svn/*</span><br></div></code></pre></div></div>
<ul>
<li class="">When joining lines, don't insert two spaces after punctuation.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set nojoinspaces</span><br></div></code></pre></div></div>
<ul>
<li class="">Don't redraw while executing macros, this is a good performance configuration.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set lazyredraw</span><br></div></code></pre></div></div>
<ul>
<li class="">Turn backup off. Since most stuff are within version control, I feel that these backup and swap files are just annoying so I disable them all.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set nobackup</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set nowritebackup</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set noswapfile</span><br></div></code></pre></div></div>
<ul>
<li class="">Set default 1 tab == 4 spaces</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set shiftwidth=4</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set tabstop=4</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set softtabstop=4</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set smartindent</span><br></div></code></pre></div></div>
<ul>
<li class="">Create undo file to keep history after closing the file.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set undofile</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set undolevels=100</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set undodir=~/.vim/undo//</span><br></div></code></pre></div></div>
<ul>
<li class="">Remember info about open buffers on close.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set viminfo^=%</span><br></div></code></pre></div></div>
<ul>
<li class="">Sometimes I lose track of the line that I am on, so enabling <code>cursorline</code> make it much much easier.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set cursorline</span><br></div></code></pre></div></div>
<ul>
<li class="">Enable Omni completion.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set omnifunc=syntaxcomplete#Complete</span><br></div></code></pre></div></div>
<ul>
<li class="">Disable the visual bell.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set t_vb=</span><br></div></code></pre></div></div>
<ul>
<li class="">Set number of colours to be used to 256, as I like it so.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set t_Co=256</span><br></div></code></pre></div></div>
<ul>
<li class="">Clearing uses the current background colour.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set t_ut=</span><br></div></code></pre></div></div>
<ul>
<li class="">Set vertical Cursor in insert mode.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set guicursor=n-v-c:block,i-ci-ve:ver25,r-cr:hor20,o:hor50</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  \,a:blinkwait700-blinkoff400-blinkon250-Cursor/lCursor</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  \,sm:block-blinkwait175-blinkoff150-blinkon175</span><br></div></code></pre></div></div>
<a href="https://ammar-najjar.com/blog/vimrc-adventure#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="plugins-manager">Plugins Manager<a href="https://ammar-najjar.com/blog/vimrc-adventure#plugins-manager" class="hash-link" aria-label="Direct link to Plugins Manager" title="Direct link to Plugins Manager" translate="no">​</a></h2>
<p>For plugins, I tried many plugins managers, I used to use <a href="https://github.com/VundleVim/Vundle.vim" target="_blank" rel="noopener noreferrer" class="">vundle</a>, and the set it up in my vimrc should have been looking like:</p>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">filetype off</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set rtp+=~/.vim/bundle/Vundle.vim</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">call vundle#begin()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Plugin 'VundleVim/Vundle.vim'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">" add other plugins here</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">call vundle#end()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">filetype plugin indent on</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">syntax on</span><br></div></code></pre></div></div>
<p>Then I switched to another plugin manager called <a href="https://github.com/vim-scripts/vim-plug" target="_blank" rel="noopener noreferrer" class="">vim-plug</a> which is faster and do asynchronous updating for the plugins. The setup is also simpler, and works fine with both vim/neovim:</p>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">call plug#begin(s:editor_root."/plugged/")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">" add plugins here</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">" Plug 'dev-name/repo-name'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">call plug#end()</span><br></div></code></pre></div></div>
<p>I am not going to discuss my favorite plugins and their configurations here, as it differs from time to time, and depends on the use of vim and the working environment. I also moved all the plugins related stuff into another file <code>plugs.vim</code>, so I don't get so distracted with them and their settings.</p>
<a href="https://ammar-najjar.com/blog/vimrc-adventure#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="mappings">Mappings<a href="https://ammar-najjar.com/blog/vimrc-adventure#mappings" class="hash-link" aria-label="Direct link to Mappings" title="Direct link to Mappings" translate="no">​</a></h2>
<ul>
<li class="">Neovim terminal mode mappings, to act on terminal buffer similar to a normal vim buffer.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">tnoremap &lt;Esc&gt; &lt;C-\&gt;&lt;C-n&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tnoremap &lt;C-h&gt; &lt;C-\&gt;&lt;C-n&gt;&lt;C-w&gt;h</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tnoremap &lt;C-j&gt; &lt;C-\&gt;&lt;C-n&gt;&lt;C-w&gt;j</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tnoremap &lt;C-k&gt; &lt;C-\&gt;&lt;C-n&gt;&lt;C-w&gt;k</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">tnoremap &lt;C-l&gt; &lt;C-\&gt;&lt;C-n&gt;&lt;C-w&gt;l</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">noremap &lt;leader&gt;s :split term://bash&lt;CR&gt;&lt;C-w&gt;&lt;S-j&gt;&lt;S-a&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">noremap &lt;leader&gt;t :tabedit term://bash&lt;CR&gt;&lt;S-a&gt;</span><br></div></code></pre></div></div>
<ul>
<li class="">Many times I want to stay in visual mode when I do a shifting for a block of code, so I remapped the original keys to do the trick.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">vnoremap &lt; &lt;gv</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">vnoremap &gt; &gt;gv</span><br></div></code></pre></div></div>
<ul>
<li class="">Move a line of text up and down using <code>leader + [jk]</code> in both normal and visual mode.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">nmap &lt;leader&gt;j mz:m+&lt;cr&gt;'z</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">nmap &lt;leader&gt;k mz:m-2&lt;cr&gt;'z</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">vmap &lt;leader&gt;j :m'&gt;+&lt;cr&gt;`&lt;my`&gt;mzgv`yo`z</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">vmap &lt;leader&gt;k :m'&lt;-2&lt;cr&gt;`&gt;my`&lt;mzgv`yo`z</span><br></div></code></pre></div></div>
<ul>
<li class="">Switch between characters next to each other using <code>leader + [hl]</code> for I use it a lot to fix some hastly typos in normal mode.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">nmap &lt;leader&gt;l xp</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">nmap &lt;leader&gt;h xhhp</span><br></div></code></pre></div></div>
<ul>
<li class="">Disable highlighted matches from previous search when <code>leader + ENTER</code> is pressed.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">map &lt;silent&gt; &lt;leader&gt;&lt;CR&gt; :noh&lt;CR&gt;</span><br></div></code></pre></div></div>
<ul>
<li class="">I use windows quite often, so this is a smart way to move between windows, using the <code>ctrl</code> with combination of the original vim movement keys <code>[hjkl]</code>.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">map &lt;C-j&gt; &lt;C-W&gt;j</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">map &lt;C-k&gt; &lt;C-W&gt;k</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">map &lt;C-h&gt; &lt;C-W&gt;h</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">map &lt;C-l&gt; &lt;C-W&gt;l</span><br></div></code></pre></div></div>
<ul>
<li class="">Open a new tab with the current buffer's path. Super useful when editing files in the same directory</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">map &lt;leader&gt;te :tabedit &lt;c-r&gt;=expand("%:p:h")&lt;CR&gt;/</span><br></div></code></pre></div></div>
<ul>
<li class="">Switch <code>cwd</code> to the directory of the open buffer. Coupled with the previous mapping, I get a very pleasant behaviour.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">map &lt;leader&gt;cd :cd %:p:h&lt;CR&gt;:pwd&lt;CR&gt;</span><br></div></code></pre></div></div>
<ul>
<li class="">Open vimrc file in a new tabe mapping.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">nmap &lt;leader&gt;ev :tabe $MYVIMRC&lt;CR&gt;</span><br></div></code></pre></div></div>
<ul>
<li class="">Toggle spell checking on and off, when I am writing a paper.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">map &lt;leader&gt;ss :setlocal spell!&lt;cr&gt;</span><br></div></code></pre></div></div>
<a href="https://ammar-najjar.com/blog/vimrc-adventure#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="status-line">Status Line<a href="https://ammar-najjar.com/blog/vimrc-adventure#status-line" class="hash-link" aria-label="Direct link to Status Line" title="Direct link to Status Line" translate="no">​</a></h2>
<p>I have a very nice status line, which I am proud of, and prefer it over status line fancy plugins. It contains some helper functions and plugins status, so if you don't use those plugins, just comment the lines for those, <a href="https://github.com/tpope/vim-fugitive" target="_blank" rel="noopener noreferrer" class="">Fugitive</a> and <a href="https://github.com/scrooloose/syntastic" target="_blank" rel="noopener noreferrer" class="">Syntastic</a>.</p>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">set statusline=</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline=[%n]\                                            " buffer number</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%&lt;%.99f                                         " File name, F for full path</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%#warningmsg#                                   " display a warning if</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%{HasPaste()}                                   " File name, F for full path</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%*                                              " tab chars</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%m%r%h%w                                        " status flags</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%#question#                                     " Display a warning if</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%{(&amp;fenc!='utf-8'&amp;&amp;&amp;fenc!='')?'['.&amp;fenc.']':''} " file encoding isnt</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%*                                              " utf-8</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%#warningmsg#                                   " display a warning if</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%{StatuslineTabWarning()}                       " files contains</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%*                                              " tab chars</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%#question#                                     " Display a warning if</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%{fugitive#statusline()}                        " Fugitive</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%*                                              " tab chars</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%=                                              " right align remainder</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%{SyntasticStatuslineFlag()}                    " Syntastic</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%y                                              " buffer file type</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%#directory#                                    " display a warning if</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%{&amp;ff!='unix'?'['.&amp;ff.']':''}                   " fileformat isnt</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%*                                              " unix</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%c%V,%l/                                        " column and row Number</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set statusline+=%L\ %P                                          " total lines, position in file</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">set laststatus=2</span><br></div></code></pre></div></div>
<p>I like to change the color of the statusline to blue when I go insert mode, and this is done by the following lines:</p>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">autocmd InsertEnter * highlight StatusLine term=reverse ctermbg=Blue gui=bold guifg=White guibg=Blue</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">autocmd InsertLeave * highlight StatusLine term=reverse ctermfg=254 ctermbg=238 gui=bold guifg=White guibg=Black</span><br></div></code></pre></div></div>
<a href="https://ammar-najjar.com/blog/vimrc-adventure#" class="go-to-top">Go to Top</a>
<h2 class="anchor anchorTargetStickyNavbar_Vzrq" id="useful-functions">Useful Functions<a href="https://ammar-najjar.com/blog/vimrc-adventure#useful-functions" class="hash-link" aria-label="Direct link to Useful Functions" title="Direct link to Useful Functions" translate="no">​</a></h2>
<ul>
<li class="">Delete trailing white space on save, for all filetypes except markdown, for sometimes, I need to leave two spaces at the end of the line to make a soft line break.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">func! DeleteTrailingWS()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    " Don't strip on these filetypes</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    if &amp;ft =~ 'markdown'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        return</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    endif</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    exe "normal mz"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    %s/\s\+$//ge</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    exe "normal `z"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">endfunc</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">autocmd BufWrite *.* :call DeleteTrailingWS()</span><br></div></code></pre></div></div>
<ul>
<li class="">Convenient command to see the difference between the current buffer and the file it was loaded from, thus the changes you made.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">if !exists(":Diff")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    command DiffOrig vert new | set bt=nofile | r ++edit # | 0d_ | diffthis</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">                \ | wincmd p | diffthis</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">endif</span><br></div></code></pre></div></div>
<ul>
<li class="">Return to last edit position when opening files (You want this!)</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">autocmd BufReadPost *</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            \ if line("'\"") &gt; 0 &amp;&amp; line("'\"") &lt;= line("$") |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            \   exe "normal! g`\"" |</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            \ endif</span><br></div></code></pre></div></div>
<ul>
<li class="">Visual mode pressing <code>*</code> or <code>#</code> searches for the current selection.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">vnoremap &lt;silent&gt; * :call VisualSelection('f')&lt;CR&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">vnoremap &lt;silent&gt; # :call VisualSelection('b')&lt;CR&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function! CmdLine(str)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    exe "menu Foo.Bar :" . a:str</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    emenu Foo.Bar</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    unmenu Foo</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">endfunction</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function! VisualSelection(direction) range</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    let l:saved_reg = @"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    execute "normal! vgvy"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    let l:pattern = escape(@", '\\/.*$^~[]')</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    let l:pattern = substitute(l:pattern, "\n$", "", "")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    if a:direction == 'b'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        execute "normal ?" . l:pattern . "^M"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    elseif a:direction == 'gv'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        call CmdLine("vimgrep " . '/'. l:pattern . '/' . ' **/*.')</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    elseif a:direction == 'replace'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        call CmdLine("%s" . '/'. l:pattern . '/')</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    elseif a:direction == 'f'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        execute "normal /" . l:pattern . "^M"</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    endif</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    let @/ = l:pattern</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    let @" = l:saved_reg</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">endfunction</span><br></div></code></pre></div></div>
<ul>
<li class="">Java compile using javac and run using java applied on the current buffer. Useful for small java programs to run on the fly.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">function! CompileAndRunJava()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    :w!</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    setlocal makeprg=javac\ %</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    setlocal errorformat=%A%f:%l:\ %m,%-Z%p^,%-C%.%#</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    :make</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    " split source filename by . and pass the first part to java</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    :!i=%; echo ${i//.*/}|xargs java</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">endfunction</span><br></div></code></pre></div></div>
<ul>
<li class="">In my status line, I use some functions. One is to get the state of the current buffer, weather it uses tabs or spaces.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">function! StatuslineTabWarning()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    if !exists("b:statusline_tab_warning")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        let tabs = search('^\t', 'nw') != 0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        if tabs</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            let b:statusline_tab_warning = '[tabs]'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        else</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">            let b:statusline_tab_warning = ''</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        endif</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    endif</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    return b:statusline_tab_warning</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">endfunction</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">autocmd cursorhold,bufwritepost * unlet! b:statusline_tab_warning</span><br></div></code></pre></div></div>
<ul>
<li class="">This is to checks if paste mode is enabled, I like to see that in my statusline as well.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">function! HasPaste()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    if &amp;paste</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        return '[PASTE]'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    else</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        return ''</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    endif</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">endfunction</span><br></div></code></pre></div></div>
<ul>
<li class="">Don't close window, when deleting a buffer.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">command! Bclose call &lt;SID&gt;BufcloseCloseIt()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function! &lt;SID&gt;BufcloseCloseIt()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    let l:currentBufNum = bufnr("%")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    let l:alternateBufNum = bufnr("#")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    if buflisted(l:alternateBufNum)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        buffer #</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    else</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        bnext</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    endif</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    if bufnr("%") == l:currentBufNum</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        new</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    endif</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain" style="display:inline-block"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    if buflisted(l:currentBufNum)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        execute("bdelete! ".l:currentBufNum)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    endif</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">endfunction</span><br></div></code></pre></div></div>
<ul>
<li class="">As everyone has his own standards for using either tabs or spaces, this function allow toggling between tabs and spaces, so ease the hussle.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">function! TabToggle()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    if &amp;expandtab</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        set noexpandtab</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        retab!</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    else</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        set expandtab</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        retab</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    endif</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">endfunction</span><br></div></code></pre></div></div>
<ul>
<li class="">Show red highlighting on ColumnColor 80.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">function! g:ToggleColorColumn()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  if &amp;colorcolumn != ''</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    setlocal colorcolumn&amp;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  else</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    setlocal colorcolumn=80</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  endif</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">endfunction</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">nnoremap &lt;silent&gt; &lt;leader&gt;cc :call g:ToggleColorColumn()&lt;CR&gt;</span><br></div></code></pre></div></div>
<ul>
<li class="">Many times when I search for a regex, I wanted to copy all the existing matches and paste them somewhere else, that's why I use this function.<!-- -->
<ul>
<li class=""><code>:CopyMatches</code> to copy all matches to the clipboard.</li>
<li class=""><code>:CopyMatches x</code> where <code>x</code> is any register to hold the result.</li>
<li class="">paste from register x with <code>"xp</code> or <code>"xP</code>.</li>
</ul>
</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">function! CopyMatches(reg)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    let hits = []</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    %s//\=len(add(hits, submatch(0))) ? submatch(0) : ''/ge</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    let reg = empty(a:reg) ? '+' : a:reg</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    execute 'let @'.reg.' = join(hits, "\n") . "\n"'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">endfunction</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">command! -register CopyMatches call CopyMatches(&lt;q-reg&gt;)</span><br></div></code></pre></div></div>
<ul>
<li class="">Append modeline after last line in buffer.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">function! AppendModeline()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  let l:modeline = printf(" vim: set ft=%s ts=%d sw=%d %set %sai : ",</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">        \ &amp;filetype, &amp;tabstop, &amp;shiftwidth, &amp;expandtab ? '' : 'no', &amp;autoindent ? '' : 'no')</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  let l:modeline = substitute(&amp;commentstring, "%s", l:modeline, "")</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  call append(line("$"), l:modeline)</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">endfunction</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">nnoremap &lt;silent&gt; &lt;Leader&gt;ml :call AppendModeline()&lt;CR&gt;</span><br></div></code></pre></div></div>
<ul>
<li class="">Realign buffers when iterm goes fullscreen</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">augroup FixProportionsOnResize</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  au!</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  au VimResized * exe "normal! \&lt;c-w&gt;="</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">augroup END</span><br></div></code></pre></div></div>
<ul>
<li class="">Highlight all instances of word under cursor, when idle. Useful when studying strange source code. Type <code>z/</code> to toggle highlighting on/off.</li>
</ul>
<div class="language-vim codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-vim codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">nnoremap z/ :if AutoHighlightToggle()&lt;Bar&gt;set hls&lt;Bar&gt;endif&lt;CR&gt;</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">function! AutoHighlightToggle()</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  let @/ = ''</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  if exists('#auto_highlight')</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    au! auto_highlight</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    augroup! auto_highlight</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    setl updatetime=100</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    echo 'Highlight current word: off'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    return 0</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  else</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    augroup auto_highlight</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      au!</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">      au CursorHold * let @/ = '\V\&lt;'.escape(expand('&lt;cword&gt;'), '\').'\&gt;'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    augroup end</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    setl updatetime=100</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    echo 'Highlight current word: ON'</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">    return 1</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">  endif</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">endfunction</span><br></div></code></pre></div></div>
<p>All information for vim commands can be found using the help system in vim by typing <code>:help command</code> or look at the online <a href="http://vimdoc.sourceforge.net/htmldoc/options.html" target="_blank" rel="noopener noreferrer" class="">docs</a> for they are very plain and clear.
The complete set of my vimrc is on my <a href="https://github.com/ammarnajjar/dotfiles/blob/master/init.vim" target="_blank" rel="noopener noreferrer" class="">github</a>. There I use one <a href="https://github.com/ammarnajjar/dotfiles" target="_blank" rel="noopener noreferrer" class="">repo</a> for all my dotfiles, bash and tmux configutaions.</p>
<p>Feel free to suggest improvements.</p>
<a href="https://ammar-najjar.com/blog/vimrc-adventure#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Neovim</category>
            <category>Vim</category>
        </item>
        <item>
            <title><![CDATA[Opencv on Openshift]]></title>
            <link>https://ammar-najjar.com/blog/opencv-on-openshift</link>
            <guid>https://ammar-najjar.com/blog/opencv-on-openshift</guid>
            <pubDate>Fri, 17 Jun 2016 18:02:02 GMT</pubDate>
            <description><![CDATA[How to install OpenCV on a Python 2.7 OpenShift cartridge while staying within the 1 GB quota limit, with a complete bash script for the setup.]]></description>
            <content:encoded><![CDATA[<p>When I wanted to intall opencv on my python-2.7 openshift online cartridge I faced the problem of quota limitation to 1GB.</p>
<!-- -->
<p>I found this <a href="https://codingexodus.blogspot.de/2013/04/how-to-install-opencv-on-openshift.html" target="_blank" rel="noopener noreferrer" class="">blog post</a> by Stephen Nneji discussed how to use DIY cartridge and install python and opencv manually. Another <a href="http://kumarcode.com/How-to-install-OpenCV-on-OpenShift/" target="_blank" rel="noopener noreferrer" class="">blog post</a> by Nikhil Kumar used the Python-2.7 cartridge template to reduce the size of compiled opencv.</p>
<p>I collected the complete steps and put them in a single <a href="https://github.com/ammarnajjar/opencv_on_openshift_online/blob/master/install.sh" target="_blank" rel="noopener noreferrer" class="">bash script</a>.</p>
<p>To run this script in your openshift Python-2.7 cartridge, run the following command:</p>
<div class="language-bash codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-bash codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token plain">$ wget https://raw.githubusercontent.com/ammarnajjar/opencv_on_openshift_online/master/install.sh &amp;&amp; bash install.sh</span><br></div></code></pre></div></div>
<p>Then test importing opencv:</p>
<div class="language-python codeBlockContainer_Ckt0 theme-code-block" style="--prism-color:#393A34;--prism-background-color:#f6f8fa"><div class="codeBlockContent_QJqH"><pre tabindex="0" class="prism-code language-python codeBlock_bY9V thin-scrollbar" style="color:#393A34;background-color:#f6f8fa"><code class="codeBlockLines_e6Vv"><div class="token-line" style="color:#393A34"><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> python</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Python </span><span class="token number" style="color:#36acaa">2.7</span><span class="token number" style="color:#36acaa">.11</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">default</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> Mar </span><span class="token number" style="color:#36acaa">31</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">2016</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">20</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">46</span><span class="token punctuation" style="color:#393A34">:</span><span class="token number" style="color:#36acaa">51</span><span class="token punctuation" style="color:#393A34">)</span><span class="token plain"> </span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token punctuation" style="color:#393A34">[</span><span class="token plain">GCC </span><span class="token number" style="color:#36acaa">5.3</span><span class="token number" style="color:#36acaa">.1</span><span class="token plain"> </span><span class="token number" style="color:#36acaa">20151207</span><span class="token plain"> </span><span class="token punctuation" style="color:#393A34">(</span><span class="token plain">Red Hat </span><span class="token number" style="color:#36acaa">5.3</span><span class="token number" style="color:#36acaa">.1</span><span class="token operator" style="color:#393A34">-</span><span class="token number" style="color:#36acaa">2</span><span class="token punctuation" style="color:#393A34">)</span><span class="token punctuation" style="color:#393A34">]</span><span class="token plain"> on linux2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain">Type </span><span class="token string" style="color:#e3116c">"help"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"copyright"</span><span class="token punctuation" style="color:#393A34">,</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"credits"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">or</span><span class="token plain"> </span><span class="token string" style="color:#e3116c">"license"</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">for</span><span class="token plain"> more information</span><span class="token punctuation" style="color:#393A34">.</span><span class="token plain"></span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">&gt;&gt;</span><span class="token operator" style="color:#393A34">&gt;</span><span class="token plain"> </span><span class="token keyword" style="color:#00009f">import</span><span class="token plain"> cv2</span><br></div><div class="token-line" style="color:#393A34"><span class="token plain"></span><span class="token operator" style="color:#393A34">&gt;&gt;</span><span class="token operator" style="color:#393A34">&gt;</span><br></div></code></pre></div></div>
<a href="https://ammar-najjar.com/blog/opencv-on-openshift#" class="go-to-top">Go to Top</a>]]></content:encoded>
            <category>Python</category>
            <category>Opencv</category>
            <category>openshift</category>
        </item>
    </channel>
</rss>