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 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KCtkkFDkSGi868JWuinwcF
This commit is contained in:
PeterChrz 2026-09-07 10:46:56 -04:00
commit 40c23f9c6b
Signed by untrusted user who does not match committer: pch
GPG key ID: 8F0826ECF7302C63
18 changed files with 756 additions and 0 deletions

32
.gitignore vendored Normal file
View file

@ -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

24
Makefile Normal file
View file

@ -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

19
Mocpp.sublime-project Normal file
View file

@ -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]
}
}

290
README.md Normal file
View file

@ -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 <this repo> ~/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 `<name>.bak-<timestamp>` 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`.

7
ch1/.clangd Normal file
View file

@ -0,0 +1,7 @@
Diagnostics:
ClangTidy:
Add:
- performance-*
- cppcoreguidelines-*
Remove:
- cppcoreguidelines-avoid-magic-numbers

9
ch1/1-1.cpp Normal file
View file

@ -0,0 +1,9 @@
#include <iostream>
#include <vector>
int main() {
std::vector<int> values{1,2,3,4,5};
for (int v : values) {
std::cout << v << '\n';
}
}

7
ch1/1-4.cpp Normal file
View file

@ -0,0 +1,7 @@
#include <algorithm>
#include <vector>
int main() {
std::vector<int> v{1,2,3,4};
auto count = std::count_if(v.begin(), v.end(),
[](int x){ return x % 2 == 0; });
}

4
ch1/compile_flags.txt Normal file
View file

@ -0,0 +1,4 @@
-std=c++20
-Wall
-Wextra
-Wconversion

4
compile_flags.txt Normal file
View file

@ -0,0 +1,4 @@
-std=c++20
-Wall
-Wextra
-Wconversion

0
inputf.in Normal file
View file

206
setup.sh Executable file
View file

@ -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 <<EOF
Open the project: subl --project "$REPO/Mocpp.sublime-project"
Build: Ctrl+B
Contest layout: Ctrl+Alt+Shift+C
Select build: Tools > 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

8
source.cpp Normal file
View file

@ -0,0 +1,8 @@
#include <iostream>
#include <vector>
int main() {
std::vector<int> values{1,2,3,4,5};
for (int v : values) {
std::cout << v << '\n';
}
}

35
sublime/CP.sublime-build Normal file
View file

@ -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}]\""
}
]
}

View file

@ -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"
}
}

View file

@ -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)\""
}
]
}

20
sublime/keymap.json Normal file
View file

@ -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]] }
}
]

32
sublime/layout.json Normal file
View file

@ -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
]
}

12
sublime/packages.json Normal file
View file

@ -0,0 +1,12 @@
[
"LSP",
"LSP-clangd",
"MarkdownPreview",
"MarkdownPreviewEnhanced",
"Package Control",
"Phpactor",
"Python 3",
"Theme - Midnight",
"Theme - Monokai Pro",
"phpfmt"
]