diff --git a/Makefile b/Makefile index 20104b26..7726273c 100644 --- a/Makefile +++ b/Makefile @@ -18,7 +18,7 @@ SHELL_INSTALL_DIR=$(DATA_DIR)/quickshell/dms ASSETS_DIR=assets APPLICATIONS_DIR=$(DATA_DIR)/applications -.PHONY: all build clean install install-bin install-shell install-completions install-systemd install-icon install-desktop uninstall uninstall-bin uninstall-shell uninstall-completions uninstall-systemd uninstall-icon uninstall-desktop help +.PHONY: all build clean lint-qml install install-bin install-shell install-completions install-systemd install-icon install-desktop uninstall uninstall-bin uninstall-shell uninstall-completions uninstall-systemd uninstall-icon uninstall-desktop help all: build @@ -32,6 +32,9 @@ clean: @$(MAKE) -C $(CORE_DIR) clean @echo "Clean complete" +lint-qml: + @./quickshell/scripts/qmllint-entrypoints.sh + # Installation targets install-bin: @echo "Installing $(BINARY_NAME) to $(INSTALL_DIR)..." @@ -130,6 +133,7 @@ help: @echo " all (default) - Build the DMS binary" @echo " build - Same as 'all'" @echo " clean - Clean build artifacts" + @echo " lint-qml - Run qmllint on shell entrypoints using the Quickshell tooling VFS" @echo "" @echo "Install:" @echo " install - Build and install everything (requires sudo)" diff --git a/quickshell/AGENTS.md b/quickshell/AGENTS.md index 99da93a0..81e555a2 100644 --- a/quickshell/AGENTS.md +++ b/quickshell/AGENTS.md @@ -99,7 +99,7 @@ qs -v -p shell.qml # Verbose debugging # Code formatting and linting qmlfmt -t 4 -i 4 -b 250 -w /path/to/file.qml # Format QML (don't use qmlformat) -qmllint **/*.qml # Lint all QML files +make -C .. lint-qml # From quickshell/, call the repo-root lint target; requires the generated .qmlls.ini VFS from `qs -p .` ./qmlformat-all.sh # Format all QML files ``` @@ -783,7 +783,7 @@ When modifying the shell: **QML Frontend:** 1. **Test changes**: `qs -p .` (automatic reload on file changes) -2. **Code quality**: Run `./qmlformat-all.sh` or `qmlformat -i **/*.qml` and `qmllint **/*.qml` +2. **Code quality**: Run `./qmlformat-all.sh` or `qmlformat -i **/*.qml`, then from repo root run `make lint-qml` after Quickshell has generated the local `.qmlls.ini` VFS with `qs -p .` 3. **Performance**: Ensure animations remain smooth (60 FPS target) 4. **Theming**: Use `Theme.propertyName` for Material Design 3 consistency diff --git a/quickshell/README.md b/quickshell/README.md index f52854bf..acf84d0d 100644 --- a/quickshell/README.md +++ b/quickshell/README.md @@ -27,7 +27,7 @@ quickshell -p quickshell/ **Code formatting:** ```bash qmlfmt -t 4 -i 4 -b 250 -w path/to/file.qml -qmllint **/*.qml +make lint-qml # Run from repo root; requires a generated quickshell/.qmlls.ini VFS from `qs -p quickshell/` ``` ## Components diff --git a/quickshell/scripts/qmllint-entrypoints.sh b/quickshell/scripts/qmllint-entrypoints.sh new file mode 100755 index 00000000..676b466a --- /dev/null +++ b/quickshell/scripts/qmllint-entrypoints.sh @@ -0,0 +1,97 @@ +#!/usr/bin/env bash +set -euo pipefail + +script_dir="$( + CDPATH='' + cd -- "$(dirname -- "${BASH_SOURCE[0]}")" && pwd +)" +repo_root="$( + CDPATH='' + cd -- "${script_dir}/../.." && pwd +)" +quickshell_dir="${repo_root}/quickshell" +qmllint_bin="${QMLLINT:-qmllint}" +qmlls_config="${quickshell_dir}/.qmlls.ini" + +if ! command -v -- "${qmllint_bin}" >/dev/null 2>&1; then + printf 'error: qmllint not found in PATH (override with QMLLINT=/path/to/qmllint)\n' >&2 + exit 127 +fi + +trim_ini_value() { + local value="$1" + value="${value#\"}" + value="${value%\"}" + printf '%s\n' "${value}" +} + +read_ini_value() { + local key="$1" + local file="$2" + local raw + + raw="$(sed -n "s/^${key}=//p" "${file}" | head -n 1)" + if [[ -z "${raw}" ]]; then + return 1 + fi + + trim_ini_value "${raw}" +} + +print_vfs_recovery() { + printf 'Generate it by starting the local shell config once, for example:\n' >&2 + printf ' dms -c %q run\n' "${quickshell_dir}" >&2 + printf ' qs -p %q\n' "${quickshell_dir}" >&2 +} + +if [[ ! -e "${qmlls_config}" ]]; then + printf 'error: %s is missing. lint-qml requires the Quickshell tooling VFS.\n' "${qmlls_config}" >&2 + print_vfs_recovery + exit 1 +fi + +if ! build_dir="$(read_ini_value "buildDir" "${qmlls_config}")"; then + printf 'error: %s does not contain a buildDir entry.\n' "${qmlls_config}" >&2 + print_vfs_recovery + exit 1 +fi + +if ! import_paths_raw="$(read_ini_value "importPaths" "${qmlls_config}")"; then + printf 'error: %s does not contain an importPaths entry.\n' "${qmlls_config}" >&2 + print_vfs_recovery + exit 1 +fi + +if [[ ! -d "${build_dir}" || ! -f "${build_dir}/qs/qmldir" ]]; then + printf 'error: Quickshell tooling VFS is missing or stale: %s\n' "${build_dir}" >&2 + print_vfs_recovery + exit 1 +fi + +targets=( + "${quickshell_dir}/shell.qml" + "${quickshell_dir}/DMSShell.qml" + "${quickshell_dir}/DMSGreeter.qml" +) + +qmllint_args=( + --ignore-settings + -W 0 + -I "${build_dir}" +) + +IFS=':' read -r -a import_paths <<< "${import_paths_raw}" +for path in "${import_paths[@]}"; do + if [[ -n "${path}" ]]; then + qmllint_args+=(-I "${path}") + fi +done + +if ! output="$("${qmllint_bin}" "${qmllint_args[@]}" "${targets[@]}" 2>&1)"; then + printf '%s\n' "${output}" >&2 + exit 1 +fi + +if [[ -n "${output}" ]]; then + printf '%s\n' "${output}" +fi