commit 40c23f9c6b3dd936a59c6b2dbfe3a038fe8d7a55 Author: PeterChrz Date: Mon Sep 7 10:46:56 2026 -0400 Sublime Text + clang C++ setup with portable installer Build systems, Makefile, and a setup script that reproduces this environment on another machine. - CP.sublime-build: incremental make-based build, clang++, flags shared with clangd via compile_flags.txt - compete_CPP.sublime-build: competitive-programming workflow, runs against inputf.in and diffs the result against expectedf.out - Makefile: builds every .cpp in the tree into a binary beside its source - setup.sh: export/install/check, path-neutral payload, idempotent - README: setup notes, the Sublime $-expansion trap, and the -MMD -MP header-dependency limitation to fix later The .sublime-workspace is deliberately excluded: it holds Sublime's global recent-file history and absolute paths. Only the pane layout is portable, extracted to sublime/layout.json. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01KCtkkFDkSGi868JWuinwcF diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..9be8afe --- /dev/null +++ b/.gitignore @@ -0,0 +1,32 @@ +# Compiled binaries have no file extension, so a normal pattern cannot catch +# them. Ignore everything, then whitelist the file types worth committing. +* +!*/ + +# sources and headers +!*.cpp +!*.h +!*.hpp + +# config that should travel with the repo +!Makefile +!setup.sh +!.gitignore +!.clangd +!*.md +!*.txt +!*.json +!*.sublime-project +!*.sublime-build +!*.sublime-settings +!*.sublime-keymap + +# contest input / expected answers are worth keeping +!*.in +!expectedf.out + +# ...but never these +*.sublime-workspace +*.d +*.bak-* +outputf.out diff --git a/Makefile b/Makefile new file mode 100644 index 0000000..6e74b0f --- /dev/null +++ b/Makefile @@ -0,0 +1,24 @@ +# Builds every .cpp in this tree into a binary next to its source. +# Incremental: a file is recompiled only when its .cpp is newer than its binary. +# Flags come from the compile_flags.txt nearest the source (the file clangd +# reads), falling back to the one at the repo root. +CXX := clang++ +ROOT := $(dir $(abspath $(lastword $(MAKEFILE_LIST)))) +SRCS := $(shell find $(ROOT) -name '*.cpp') +BINS := $(basename $(SRCS)) + +# The `@:` recipe is a deliberate no-op. Without it, make prints +# "Nothing to be done for 'all'" whenever everything is already up to date. +all: $(BINS) + @: + +%: %.cpp + @flags=$$(cat $(dir $<)compile_flags.txt 2>/dev/null || cat $(ROOT)compile_flags.txt 2>/dev/null); \ + echo "compiling $(notdir $<)"; \ + $(CXX) $$flags $< -o $@ + +clean: + @rm -f $(BINS) + @echo "cleaned" + +.PHONY: all clean diff --git a/Mocpp.sublime-project b/Mocpp.sublime-project new file mode 100644 index 0000000..0eb3624 --- /dev/null +++ b/Mocpp.sublime-project @@ -0,0 +1,19 @@ +{ + "folders": + [ + { + "path": ".", + "folder_exclude_patterns": [".cache", "build"], + // binaries have no extension, so exclude them by name as you add them + "file_exclude_patterns": ["*.o", "*.d", "outputf.out"] + } + ], + "settings": + { + // clangd already indexes C++; Sublime's own indexer just duplicates it + "index_files": false, + "tab_size": 4, + "translate_tabs_to_spaces": false, + "rulers": [80] + } +} diff --git a/README.md b/README.md new file mode 100644 index 0000000..2b75af1 --- /dev/null +++ b/README.md @@ -0,0 +1,290 @@ +# Mocpp — C++ learning exercises + +Chapter exercises, one binary per `.cpp`. Built with **clang++**, edited in +Sublime Text with **clangd** for diagnostics and completion. + +## Layout + +``` +Mocpp/ +├── Makefile # builds every .cpp in the tree +├── compile_flags.txt # fallback flags (used if a chapter dir has none) +├── README.md +├── source.cpp +└── ch1/ + ├── compile_flags.txt # flags for this chapter — clangd reads this too + ├── 1-1.cpp → 1-1 # each .cpp builds to a binary beside it + └── 1-4.cpp → 1-4 +``` + +## Building + +From Sublime: + +| Key | Action | +|-----|--------| +| `Ctrl+B` | build everything (incremental — silent if nothing changed) | +| `Ctrl+Shift+B` → *Build all + Run this file* | build, then run the focused file | +| `Ctrl+Shift+B` → *Build all + Run this file (with stdin)* | same, for `std::cin` exercises | +| `Ctrl+Shift+B` → *Run this file only* | compile just the focused file, skip make | +| `Ctrl+Shift+B` → *Clean* | delete all binaries, force a full rebuild | + +From a terminal — identical result, `make` is the single source of truth: + +```bash +make # build what changed +make clean # remove all binaries +``` + +The Sublime build file lives at +`~/.config/sublime-text/Packages/User/CP.sublime-build`. + +## Flags: one file, two consumers + +`compile_flags.txt` is read by **both** clangd (editor squiggles) and the +Makefile (the actual compile). That is deliberate — it is why the warnings you +see while typing are the warnings you get when building. Edit flags there and +nowhere else. + +Current flags: `-std=c++20 -Wall -Wextra -Wconversion` + +### Starting a new chapter + +```bash +mkdir ch2 +cp ch1/compile_flags.txt ch2/ +``` + +Without its own copy a directory falls back to the root `compile_flags.txt`, so +it still gets warnings — but keeping a copy per chapter means you can vary the +standard per chapter later. + +--- + +## TODO: add `-MMD -MP` when you start writing your own headers + +**This is the one known limitation of the current Makefile.** Read this before +you create your first `.h` file. + +The build tracks only `.cpp` timestamps. It has no idea headers exist. So if you +edit a header, **make does nothing and leaves you running a stale binary** — with +no error and no warning. Verified behaviour, not theory: + +``` +$ ./ch1/1-1 +hello v1 +$ sed -i 's/hello v1/hello v2/' ch1/greet.h # edit ONLY the header +$ make + # ← silence. nothing rebuilt. +$ ./ch1/1-1 +hello v1 # ← WRONG. still the old code. +``` + +This will cost you an hour of debugging code that is already correct. + +### The fix (tested, works) + +`-MMD` makes clang emit a `.d` file listing every header the source included. +`-MP` adds a dummy target for each header, so a *deleted* header does not break +the build. `-include $(DEPS)` feeds those back to make, which then knows the +binary depends on the headers too. + +Three changes to the `Makefile`: + +```make +BINS := $(basename $(SRCS)) +DEPS := $(addsuffix .d,$(BINS)) # 1. add this line + +%: %.cpp + @flags=$$(cat $(dir $<)compile_flags.txt 2>/dev/null || cat $(ROOT)compile_flags.txt 2>/dev/null); \ + echo "compiling $(notdir $<)"; \ + $(CXX) $$flags -MMD -MP $< -o $@ # 2. add -MMD -MP here + +-include $(DEPS) # 3. add this line + +clean: + @rm -f $(BINS) $(DEPS) # 4. also remove .d files + @echo "cleaned" +``` + +After that, editing a header rebuilds what depends on it: + +``` +$ sed -i 's/hello v1/hello v2/' ch1/greet.h +$ make +compiling 1-1.cpp +$ ./ch1/1-1 +hello v2 +``` + +The generated `ch1/1-1.d` is just a make rule: + +``` +ch1/1-1: ch1/1-1.cpp ch1/greet.h +ch1/greet.h: +``` + +The leading `-` in `-include` matters: it stops make erroring on the first build, +when no `.d` files exist yet. + +--- + +## `compete_CPP` — the contest workflow + +A second build system, for problems that read input from stdin (Codeforces, +LeetCode, or any book exercise using `std::cin`). Select it with +**Tools → Build System → compete_CPP**. + +It compiles the focused `.cpp`, feeds it `inputf.in` on stdin, writes stdout to +`outputf.out`, and prints both — so you never retype a sample input. + +| Variant | Does | +|---|---| +| `Ctrl+B` | compile, run with `inputf.in`, show input and output | +| *Run + check against expectedf.out* | same, then `diff` the answer — prints PASS or FAIL | +| *Run (type input live)* | no redirect; type input into the build panel | +| *Setup: create inputf.in / expectedf.out here* | creates the three files in the current folder | +| *DIAGNOSTIC* | prints Sublime's variables | + +The three files live **next to the source**, not at the repo root. That was the +bug in the original tutorial build: it looked for `inputf.in` in the source's +folder while the files sat one level up, so they were never read. + +Typical loop: open a problem, run *Setup*, paste the sample input into +`inputf.in` and the expected answer into `expectedf.out`, then hit +*Run + check* until it says PASS. + +``` +--- outputf.out --- +6 +--- diff (expected vs actual) --- +PASS +``` + +A wrong answer shows the diff and exits non-zero: + +``` +-99 ++6 +FAIL +``` + +A compile error stops before running, so you never diff a stale binary. + +Note this build compiles **only the focused file** — it does not use the +Makefile. That is intentional: contest problems are self-contained single files. +Use the `CP` build system for the chapter exercises. + +## Gotcha: `$` in the Sublime build file + +Sublime expands **both** `${name}` and bare `$name` in `cmd`, `shell_cmd`, and +`working_dir`. Any name it does not recognise becomes an empty string *before* +bash runs. + +So a shell variable like `$SRC` is silently deleted, and a guard such as +`if [ -z "$SRC" ]` becomes `if [ -z "" ]` — always true. The symptom is a build +that insists there is no active file no matter what you do. + +Rules for editing `CP.sublime-build`: + +- Prefer Sublime's own `${file}`, `${file_path}`, `${file_base_name}` plus + `$(...)`, and use no bash variables at all. +- Any `$` meant for bash must be escaped `\$` (written `\\$` in the JSON). +- Test a variant with the *DIAGNOSTIC* build, which prints what Sublime actually + substituted. + +Reference: https://www.sublimetext.com/docs/build_systems.html + +## Saving the window layout + +Three mechanisms, easy to confuse: + +| What | Where | Saves | +|---|---|---| +| **Project** | `Mocpp.sublime-project` | folders, settings, excludes — commit this | +| **Workspace** | `Mocpp.sublime-workspace` (auto) | open tabs, pane layout, cursors — do NOT commit | +| **Hot exit** | automatic | last session, even with no project | + +Open it once with **Project → Open Project → `Mocpp.sublime-project`**. From then +on Sublime writes a `.sublime-workspace` beside it holding your exact pane +arrangement and open files, and restores them next launch. + +Keybindings for the contest layout (code left, input top-right, output +bottom-right), in `Packages/User/Default (Linux).sublime-keymap`: + +| Key | Layout | +|---|---| +| `Ctrl+Alt+Shift+C` | three-pane contest layout | +| `Ctrl+Alt+Shift+1` | back to a single pane | + +Sublime's built-in `Alt+Shift+1..5` (columns) and `Alt+Shift+8/9` (grids) still +work; the bindings above just add a split the menu does not offer. + +If you commit this directory to git: + +``` +*.sublime-workspace +``` + +## Installing this setup on another machine + +`setup.sh` moves the whole environment. Clone the repo, run install: + +```bash +git clone ~/git/Mocpp +cd ~/git/Mocpp +./setup.sh install +``` + +| Command | Does | +|---|---| +| `./setup.sh check` | verify the toolchain, change nothing | +| `./setup.sh install` | install `sublime/` onto this machine, then build | +| `./setup.sh export` | re-capture this machine's Sublime config into `sublime/` | + +**Run `export` after changing a build system**, so the payload in `sublime/` +stays current, then commit. The script never embeds copies of the config — it +installs from `sublime/`, so there is nothing to drift out of sync. + +What install does: + +- checks for `clang++`, `clangd`, `make`, `git`, and prints the right + package-manager command for your distro if any are missing +- finds Sublime's config dir (differs on Linux and macOS) and names the keymap + per platform +- substitutes `__PROJECT_ROOT__` in the build files with wherever you cloned to, + so the repo need not live at the same path +- merges the Package Control list rather than overwriting yours, so Sublime + offers to install the missing packages on next start +- runs `make` as a smoke test and fails loudly if the toolchain is broken + +Re-running is a no-op: files are compared after substitution, so nothing is +rewritten and no `.bak` files accumulate. Anything it would genuinely overwrite +is backed up as `.bak-` first. + +### Why the workspace is not copied + +`compete.sublime-workspace` is deliberately **not** part of the payload and is +gitignored. Sublime stores your global recent-file history in it — on this +machine 17 of its 21 entries were unrelated personal paths, including documents +and TLS certificates. It also hardcodes absolute paths. + +Only the pane layout is portable, so `export` extracts just that into +`sublime/layout.json`, and `install` builds a clean workspace from it. The same +layout is also bound to `Ctrl+Alt+Shift+C`, so it survives even without a +workspace file. + +## When this setup runs out + +It is built for single-file exercises. Move to CMake generating +`compile_commands.json` once you have multiple source files linking into one +program, external libraries, or more than one build configuration: + +```bash +cmake -S . -B build -G Ninja -DCMAKE_EXPORT_COMPILE_COMMANDS=ON +ln -sf build/compile_commands.json . +``` + +Then delete `compile_flags.txt` — if both exist in a directory, +`compile_commands.json` wins and the other becomes a silently ignored second +source of truth. Point the Sublime build at `ninja -C build`. diff --git a/ch1/.clangd b/ch1/.clangd new file mode 100644 index 0000000..0fa2465 --- /dev/null +++ b/ch1/.clangd @@ -0,0 +1,7 @@ +Diagnostics: + ClangTidy: + Add: + - performance-* + - cppcoreguidelines-* + Remove: + - cppcoreguidelines-avoid-magic-numbers \ No newline at end of file diff --git a/ch1/1-1.cpp b/ch1/1-1.cpp new file mode 100644 index 0000000..5a6a42c --- /dev/null +++ b/ch1/1-1.cpp @@ -0,0 +1,9 @@ +#include +#include + +int main() { + std::vector values{1,2,3,4,5}; + for (int v : values) { + std::cout << v << '\n'; + } +} \ No newline at end of file diff --git a/ch1/1-4.cpp b/ch1/1-4.cpp new file mode 100644 index 0000000..dfcf415 --- /dev/null +++ b/ch1/1-4.cpp @@ -0,0 +1,7 @@ +#include +#include +int main() { + std::vector v{1,2,3,4}; + auto count = std::count_if(v.begin(), v.end(), + [](int x){ return x % 2 == 0; }); +} \ No newline at end of file diff --git a/ch1/compile_flags.txt b/ch1/compile_flags.txt new file mode 100644 index 0000000..ac953c4 --- /dev/null +++ b/ch1/compile_flags.txt @@ -0,0 +1,4 @@ +-std=c++20 +-Wall +-Wextra +-Wconversion \ No newline at end of file diff --git a/compile_flags.txt b/compile_flags.txt new file mode 100644 index 0000000..ac953c4 --- /dev/null +++ b/compile_flags.txt @@ -0,0 +1,4 @@ +-std=c++20 +-Wall +-Wextra +-Wconversion \ No newline at end of file diff --git a/inputf.in b/inputf.in new file mode 100644 index 0000000..e69de29 diff --git a/setup.sh b/setup.sh new file mode 100755 index 0000000..91afabf --- /dev/null +++ b/setup.sh @@ -0,0 +1,206 @@ +#!/usr/bin/env bash +# Portable installer for this C++ / Sublime Text setup. +# +# ./setup.sh export copy this machine's Sublime config into ./sublime/ +# ./setup.sh install install ./sublime/ onto this machine +# ./setup.sh check verify the toolchain and report, change nothing +# +# Design notes: +# - Configs live in ./sublime/ so they are version-controlled and travel with +# the repo. The script never embeds copies that could drift. +# - The .sublime-workspace is NEVER copied. It holds absolute paths and +# Sublime's global recent-file history, which is machine-specific and +# frequently personal. Only the pane layout is portable, so install +# regenerates a clean workspace from sublime/layout.json. +set -euo pipefail + +REPO="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)" +PAYLOAD="$REPO/sublime" +STAMP="$(date +%Y%m%d-%H%M%S)" + +c_ok() { printf ' \033[32mok\033[0m %s\n' "$*"; } +c_warn() { printf ' \033[33mwarn\033[0m %s\n' "$*"; } +c_err() { printf ' \033[31mFAIL\033[0m %s\n' "$*"; } +head_() { printf '\n\033[1m%s\033[0m\n' "$*"; } + +# ---------------------------------------------------------------- locate ST -- +sublime_dir() { + case "$(uname -s)" in + Darwin) echo "$HOME/Library/Application Support/Sublime Text" ;; + *) echo "$HOME/.config/sublime-text" ;; + esac +} +# Keymaps are named per platform. +keymap_name() { + case "$(uname -s)" in + Darwin) echo "Default (OSX).sublime-keymap" ;; + *) echo "Default (Linux).sublime-keymap" ;; + esac +} + +# ---------------------------------------------------------------- export ----- +do_export() { + local user_dir; user_dir="$(sublime_dir)/Packages/User" + head_ "Exporting Sublime config from $user_dir" + [ -d "$user_dir" ] || { c_err "not found: $user_dir"; exit 1; } + mkdir -p "$PAYLOAD" + + local f + for f in CP.sublime-build compete_CPP.sublime-build \ + LSP-clangd.sublime-settings; do + if [ -f "$user_dir/$f" ]; then + # Replace this machine's project path with a placeholder so the + # payload is portable and contains no absolute paths. + sed "s|$REPO|__PROJECT_ROOT__|g" "$user_dir/$f" > "$PAYLOAD/$f" + c_ok "$f" + else c_warn "missing, skipped: $f"; fi + done + + # Keymap is stored platform-neutral; install renames it per OS. + if [ -f "$user_dir/$(keymap_name)" ]; then + cp "$user_dir/$(keymap_name)" "$PAYLOAD/keymap.json"; c_ok "keymap.json" + fi + + # Package list, for Package Control to auto-install on the new machine. + if [ -f "$user_dir/Package Control.sublime-settings" ]; then + python3 - "$user_dir/Package Control.sublime-settings" "$PAYLOAD/packages.json" <<'PY' +import json,re,sys +s=open(sys.argv[1]).read(); s=re.sub(r'^\s*//.*$','',s,flags=re.M) +s=re.sub(r',(\s*[\]}])',r'\1',s) # tolerate trailing commas +pkgs=json.load(open(sys.argv[1]).name and __import__('io').StringIO(s)).get('installed_packages',[]) +json.dump(sorted(pkgs), open(sys.argv[2],'w'), indent=2) +print(" ok packages.json (%d packages)" % len(pkgs)) +PY + fi + + # Layout only -- extracted from the workspace, stripped of everything else. + local ws; ws="$(ls "$REPO"/*.sublime-workspace 2>/dev/null | head -1 || true)" + if [ -n "$ws" ]; then + python3 - "$ws" "$PAYLOAD/layout.json" <<'PY' +import json,sys +d=json.load(open(sys.argv[1])) +json.dump(d.get("layout",{}), open(sys.argv[2],"w"), indent=2) +print(" ok layout.json (pane layout only; history discarded)") +PY + fi + head_ "Exported to $PAYLOAD" + ls -1 "$PAYLOAD" +} + +# ---------------------------------------------------------------- check ------ +do_check() { + head_ "Toolchain" + local missing=0 t + for t in clang++ clangd make git; do + if command -v "$t" >/dev/null 2>&1; then c_ok "$t $("$t" --version 2>&1 | head -1)" + else c_err "$t not found"; missing=1; fi + done + command -v cmake >/dev/null 2>&1 && c_ok "cmake (optional)" || c_warn "cmake not found (optional)" + + head_ "Sublime Text" + local sd; sd="$(sublime_dir)" + [ -d "$sd" ] && c_ok "config dir: $sd" || c_warn "config dir absent: $sd (start Sublime once)" + command -v subl >/dev/null 2>&1 && c_ok "subl on PATH" || c_warn "subl not on PATH (optional)" + + if [ "$missing" -ne 0 ]; then + head_ "Install the missing tools" + if command -v pacman >/dev/null 2>&1; then echo " sudo pacman -S --needed clang make git" + elif command -v apt-get >/dev/null 2>&1; then echo " sudo apt-get install -y clang clangd make git" + elif command -v dnf >/dev/null 2>&1; then echo " sudo dnf install -y clang clang-tools-extra make git" + elif command -v brew >/dev/null 2>&1; then echo " brew install llvm make git" + fi + return 1 + fi + return 0 +} + +# ---------------------------------------------------------------- install ---- +# Render a payload file for this machine (placeholder -> real project path), +# then install it only if the result differs from what is already there. +# This makes re-running install a no-op instead of piling up backups. +install_file() { + local src="$1" dst="$2" tmp + tmp="$(mktemp)" + sed "s|__PROJECT_ROOT__|$REPO|g" "$src" > "$tmp" + if [ -f "$dst" ] && cmp -s "$tmp" "$dst"; then + c_ok "$(basename "$dst") (unchanged)"; rm -f "$tmp"; return 0 + fi + if [ -f "$dst" ]; then + cp "$dst" "$dst.bak-$STAMP" + c_warn "existing file backed up: $(basename "$dst").bak-$STAMP" + fi + mv "$tmp" "$dst"; chmod 644 "$dst" + c_ok "$(basename "$dst")" +} + +do_install() { + [ -d "$PAYLOAD" ] || { c_err "no payload at $PAYLOAD -- run './setup.sh export' on the source machine first"; exit 1; } + do_check || { c_err "toolchain incomplete; install the tools above and re-run"; exit 1; } + + local user_dir; user_dir="$(sublime_dir)/Packages/User" + head_ "Installing Sublime config to $user_dir" + mkdir -p "$user_dir" + + local f + for f in CP.sublime-build compete_CPP.sublime-build LSP-clangd.sublime-settings; do + [ -f "$PAYLOAD/$f" ] || continue + install_file "$PAYLOAD/$f" "$user_dir/$f" + done + + [ -f "$PAYLOAD/keymap.json" ] && install_file "$PAYLOAD/keymap.json" "$user_dir/$(keymap_name)" + + # Merge the package list rather than clobbering the user's own. + if [ -f "$PAYLOAD/packages.json" ]; then + python3 - "$PAYLOAD/packages.json" "$user_dir/Package Control.sublime-settings" <<'PY' +import json,re,os,sys,io +want=json.load(open(sys.argv[1])); dst=sys.argv[2] +have=[] +if os.path.exists(dst): + s=re.sub(r'^\s*//.*$','',open(dst).read(),flags=re.M) + s=re.sub(r',(\s*[\]}])',r'\1',s) + try: have=json.load(io.StringIO(s)).get('installed_packages',[]) + except Exception: have=[] +merged=sorted(set(have)|set(want)) +json.dump({"bootstrapped":True,"installed_packages":merged}, open(dst,'w'), indent=2) +added=sorted(set(want)-set(have)) +print(" ok Package Control list merged (%d total%s)" % + (len(merged), (", added: "+", ".join(added)) if added else ", nothing new")) +PY + fi + + # Fresh workspace with the saved layout and no inherited history. + if [ -f "$PAYLOAD/layout.json" ]; then + python3 - "$PAYLOAD/layout.json" "$REPO/compete.sublime-workspace" "$REPO" <<'PY' +import json,os,sys +layout=json.load(open(sys.argv[1])); out,root=sys.argv[2],sys.argv[3] +if os.path.exists(out): + print(" warn workspace already exists, left alone: %s" % os.path.basename(out)) +else: + json.dump({"layout":layout,"buffers":[],"groups":[{"sheets":[]} for _ in layout.get("cells",[[]])], + "folders":[{"path":root}],"file_history":[], + "build_system":"Packages/User/CP.sublime-build"}, open(out,'w'), indent=2) + print(" ok clean workspace created (layout only, no file history)") +PY + fi + + head_ "Building the project" + if make -C "$REPO" --no-print-directory; then c_ok "make succeeded"; else c_err "make failed"; exit 1; fi + + head_ "Done" + cat < Build System > CP (or compete_CPP) + + Sublime will prompt to install missing packages via Package Control + on first start. Restart it once after that. +EOF +} + +case "${1:-}" in + export) do_export ;; + install) do_install ;; + check) do_check ;; + *) sed -n '2,9p' "${BASH_SOURCE[0]}"; exit 1 ;; +esac diff --git a/source.cpp b/source.cpp new file mode 100644 index 0000000..a4a44bb --- /dev/null +++ b/source.cpp @@ -0,0 +1,8 @@ +#include +#include +int main() { +std::vector values{1,2,3,4,5}; +for (int v : values) { +std::cout << v << '\n'; +} +} \ No newline at end of file diff --git a/sublime/CP.sublime-build b/sublime/CP.sublime-build new file mode 100644 index 0000000..532c2e6 --- /dev/null +++ b/sublime/CP.sublime-build @@ -0,0 +1,35 @@ +{ + // Sublime expands BOTH ${var} and $var in cmd/shell_cmd/working_dir. + // Any $ meant for bash MUST be escaped as \$ -- see + // https://www.sublimetext.com/docs/build_systems.html + // Below, ${file} is Sublime's; every \$ is bash's. + "shell_cmd": "make --no-print-directory -C __PROJECT_ROOT__", + "file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$", + "working_dir": "__PROJECT_ROOT__", + "selector": "source.c++", + + "variants": + [ + { + "name": "Build all + Run this file", + "shell_cmd": "test -n \"${file}\" || { echo 'Click into a .cpp tab, then build.'; exit 1; }; make --no-print-directory -C __PROJECT_ROOT__ && \"\\$(dirname \"${file}\")/\\$(basename \"${file}\" .cpp)\"" + }, + { + "name": "Build all + Run this file (with stdin)", + "shell_cmd": "test -n \"${file}\" || { echo 'Click into a .cpp tab, then build.'; exit 1; }; make --no-print-directory -C __PROJECT_ROOT__ && \"\\$(dirname \"${file}\")/\\$(basename \"${file}\" .cpp)\"", + "interactive": true + }, + { + "name": "Run this file only (no rebuild of others)", + "shell_cmd": "test -n \"${file}\" || { echo 'Click into a .cpp tab, then build.'; exit 1; }; clang++ \\$(cat \"${file_path}/compile_flags.txt\" 2>/dev/null) \"${file}\" -o \"${file_path}/${file_base_name}\" && \"${file_path}/${file_base_name}\"" + }, + { + "name": "Clean", + "shell_cmd": "make --no-print-directory -C __PROJECT_ROOT__ clean && echo cleaned" + }, + { + "name": "DIAGNOSTIC - show Sublime variables", + "shell_cmd": "echo \"file = [${file}]\"; echo \"file_path = [${file_path}]\"; echo \"base_name = [${file_base_name}]\"; echo \"folder = [${folder}]\"" + } + ] +} diff --git a/sublime/LSP-clangd.sublime-settings b/sublime/LSP-clangd.sublime-settings new file mode 100644 index 0000000..2a37fa2 --- /dev/null +++ b/sublime/LSP-clangd.sublime-settings @@ -0,0 +1,10 @@ +// Settings in here override those in "LSP-clangd/LSP-clangd.sublime-settings" + +{ + "initializationOptions":{ + "clangd.clang-tidy":true, + "clangd.background-index":true, + "clangd.header-insertion":"iwyu", + "clangd.completion-style":"detailed" + } +} \ No newline at end of file diff --git a/sublime/compete_CPP.sublime-build b/sublime/compete_CPP.sublime-build new file mode 100644 index 0000000..1245f02 --- /dev/null +++ b/sublime/compete_CPP.sublime-build @@ -0,0 +1,37 @@ +{ + // Competitive-programming workflow: compile the focused .cpp, feed it + // inputf.in on stdin, capture stdout to outputf.out, and print the result. + // + // Both files live NEXT TO THE SOURCE (working_dir is the file's folder), + // not at the repo root. Use "Setup" below to create them. + // + // Sublime expands ${name} AND bare $name in shell_cmd. Every $ meant for + // bash is escaped \$ -- see https://www.sublimetext.com/docs/build_systems.html + "shell_cmd": "test -n \"${file}\" || { echo 'Click into a .cpp tab, then build.'; exit 1; }; clang++ \\$(cat compile_flags.txt 2>/dev/null) \"${file}\" -o \"${file_base_name}\" || exit 1; touch inputf.in; ./\"${file_base_name}\" < inputf.in > outputf.out; echo '--- inputf.in ---'; cat inputf.in; echo '--- outputf.out ---'; cat outputf.out", + "file_regex": "^(..[^:]*):([0-9]+):?([0-9]+)?:? (.*)$", + "working_dir": "${file_path:${folder}}", + "selector": "source.c++", + + "variants": + [ + { + // Same as above, then check the answer against expectedf.out. + "name": "Run + check against expectedf.out", + "shell_cmd": "test -n \"${file}\" || { echo 'Click into a .cpp tab, then build.'; exit 1; }; clang++ \\$(cat compile_flags.txt 2>/dev/null) \"${file}\" -o \"${file_base_name}\" || exit 1; touch inputf.in; ./\"${file_base_name}\" < inputf.in > outputf.out; echo '--- outputf.out ---'; cat outputf.out; if [ -f expectedf.out ]; then echo '--- diff (expected vs actual) ---'; if diff -u expectedf.out outputf.out; then echo 'PASS'; else echo 'FAIL'; exit 1; fi; else echo '(no expectedf.out here - run Setup to make one)'; fi" + }, + { + // No redirect: type the input straight into the build panel. + "name": "Run (type input live)", + "shell_cmd": "test -n \"${file}\" || { echo 'Click into a .cpp tab, then build.'; exit 1; }; clang++ \\$(cat compile_flags.txt 2>/dev/null) \"${file}\" -o \"${file_base_name}\" || exit 1; ./\"${file_base_name}\"", + "interactive": true + }, + { + "name": "Setup: create inputf.in / expectedf.out here", + "shell_cmd": "touch inputf.in expectedf.out outputf.out; echo \"ready in \\$(pwd):\"; ls -l inputf.in expectedf.out outputf.out" + }, + { + "name": "DIAGNOSTIC - show Sublime variables", + "shell_cmd": "echo \"file = [${file}]\"; echo \"file_path = [${file_path}]\"; echo \"base_name = [${file_base_name}]\"; echo \"cwd = \\$(pwd)\"" + } + ] +} diff --git a/sublime/keymap.json b/sublime/keymap.json new file mode 100644 index 0000000..4400183 --- /dev/null +++ b/sublime/keymap.json @@ -0,0 +1,20 @@ +[ + // Competitive-programming layout: code on the left, inputf.in top-right, + // outputf.out bottom-right. Cells are [col_start, row_start, col_end, row_end]. + { + "keys": ["ctrl+alt+shift+c"], + "command": "set_layout", + "args": + { + "cols": [0.0, 0.5, 1.0], + "rows": [0.0, 0.5, 1.0], + "cells": [[0, 0, 1, 2], [1, 0, 2, 1], [1, 1, 2, 2]] + } + }, + // Back to a single pane. + { + "keys": ["ctrl+alt+shift+1"], + "command": "set_layout", + "args": { "cols": [0.0, 1.0], "rows": [0.0, 1.0], "cells": [[0, 0, 1, 1]] } + } +] diff --git a/sublime/layout.json b/sublime/layout.json new file mode 100644 index 0000000..516d6f3 --- /dev/null +++ b/sublime/layout.json @@ -0,0 +1,32 @@ +{ + "cells": [ + [ + 0, + 0, + 1, + 2 + ], + [ + 1, + 0, + 2, + 1 + ], + [ + 1, + 1, + 2, + 2 + ] + ], + "cols": [ + 0.0, + 0.5, + 1.0 + ], + "rows": [ + 0.0, + 0.5, + 1.0 + ] +} \ No newline at end of file diff --git a/sublime/packages.json b/sublime/packages.json new file mode 100644 index 0000000..d71793c --- /dev/null +++ b/sublime/packages.json @@ -0,0 +1,12 @@ +[ + "LSP", + "LSP-clangd", + "MarkdownPreview", + "MarkdownPreviewEnhanced", + "Package Control", + "Phpactor", + "Python 3", + "Theme - Midnight", + "Theme - Monokai Pro", + "phpfmt" +] \ No newline at end of file