├── .github
└── workflows
│ ├── check.yml
│ ├── release-bin.yml
│ ├── update-nix-inputs.yml
│ └── update.yml
├── .gitignore
├── .gitmodules
├── .ortfo
└── description.md
├── .vscode
├── extensions.json
├── launch.json
├── settings.json
└── tasks.json
├── CONTRIBUTING.md
├── Justfile
├── LICENSE
├── README.md
├── cmd
└── hyprls
│ └── main.go
├── color.go
├── color_test.go
├── completion.go
├── default.nix
├── demo-colors.png
├── demo-completion.png
├── demo-hover-keyword.png
├── demo-hover.png
├── demo-symbols.png
├── flake.lock
├── flake.nix
├── go.mod
├── go.sum
├── gomod2nix.toml
├── handler.go
├── hover.go
├── hyprland_version
├── main.go
├── parser
├── data
│ ├── generate
│ │ ├── ast.json
│ │ └── main.go
│ ├── keywords.go
│ ├── keywords_test.go
│ ├── load.go
│ ├── sections.go
│ ├── sources
│ │ ├── Animations.md
│ │ ├── Binds.md
│ │ ├── Configuring-Hyprland.md
│ │ ├── Dispatchers.md
│ │ ├── Dwindle-Layout.md
│ │ ├── Environment-variables.md
│ │ ├── Example-configurations.md
│ │ ├── Expanding-functionality.md
│ │ ├── Keywords.md
│ │ ├── Master-Layout.md
│ │ ├── Monitors.md
│ │ ├── Multi-GPU.md
│ │ ├── Performance.md
│ │ ├── Permissions.md
│ │ ├── Start.md
│ │ ├── Tearing.md
│ │ ├── Uncommon-tips-&-tricks.md
│ │ ├── Using-hyprctl.md
│ │ ├── Variables.md
│ │ ├── Window-Rules.md
│ │ ├── Workspace-Rules.md
│ │ ├── XWayland.md
│ │ └── _index.md
│ └── variables.go
├── decode.go
├── decode_test.go
├── fixtures
│ ├── empty.hl
│ ├── test.hl
│ └── test.json
├── highlevel.go
├── lowlevel.go
├── lowlevel_test.go
└── utils.go
├── renovate.json
├── shell.nix
├── state.go
├── symbols.go
├── sync.go
├── unimplemented.go
├── utils.go
├── vscode-extension-pack
├── .gitignore
├── .vscode
│ └── launch.json
├── .vscodeignore
├── CHANGELOG.md
├── LICENSE
├── README.md
├── bun.lockb
├── icon.png
├── package.json
├── package.json.bak
└── vsc-extension-quickstart.md
└── vscode
├── .eslintignore
├── .eslintrc.js
├── .gitignore
├── .vscodeignore
├── LICENSE
├── README.md
├── bun.lockb
├── icon.png
├── package.json
├── scripts
└── e2e.sh
├── src
└── extension.ts
├── tsconfig.json
└── vscode-hyprlang-0.0.1.vsix
/.github/workflows/check.yml:
--------------------------------------------------------------------------------
1 | name: Build and test
2 |
3 | on:
4 | pull_request:
5 | types: [opened, reopened, synchronize]
6 |
7 | jobs:
8 | build:
9 | runs-on: ubuntu-latest
10 | steps:
11 | - name: checkout
12 | uses: actions/checkout@v4
13 |
14 | - uses: actions/setup-go@v5
15 |
16 | - name: Build
17 | run: go build -o hyprls cmd/hyprls/main.go
18 |
19 | - name: Run tests
20 | run: go test
21 |
--------------------------------------------------------------------------------
/.github/workflows/release-bin.yml:
--------------------------------------------------------------------------------
1 | name: Release pre-built binaries for new tag
2 |
3 | on:
4 | push:
5 | tags:
6 | - '*'
7 |
8 | permissions:
9 | contents: write
10 |
11 | jobs:
12 | build:
13 | runs-on: ubuntu-latest
14 | steps:
15 | - name: checkout
16 | uses: actions/checkout@v4
17 |
18 | - name: go-setup
19 | uses: actions/setup-go@v5
20 |
21 | - name: go-build
22 | run: go build -o hyprls cmd/hyprls/main.go
23 |
24 | - name: zip-artifacts
25 | run: zip hyprls.zip hyprls
26 |
27 | - name: deploy-binaries
28 | uses: softprops/action-gh-release@v2
29 | with:
30 | tag_name: ${{ github.ref_name }}
31 | files: hyprls.zip
32 | token: ${{secrets.GITHUB_TOKEN}}
33 |
--------------------------------------------------------------------------------
/.github/workflows/update-nix-inputs.yml:
--------------------------------------------------------------------------------
1 | name: Update nix inputs
2 |
3 | on:
4 | workflow_dispatch:
5 | schedule:
6 | - cron: '51 2 * * 0'
7 |
8 | jobs:
9 | update:
10 | name: inputs
11 | runs-on: ubuntu-latest
12 | steps:
13 | - name: Clone repository
14 | uses: actions/checkout@v4
15 |
16 | - uses: DeterminateSystems/nix-installer-action@main
17 | - uses: extractions/setup-just@v3
18 | - name: Update inputs
19 | run: just update-nix-inputs
20 |
21 | - name: Commit
22 | uses: stefanzweifel/git-auto-commit-action@v5
23 | with:
24 | commit_message: '⬆️ Nix: update inputs'
25 |
--------------------------------------------------------------------------------
/.github/workflows/update.yml:
--------------------------------------------------------------------------------
1 | name: Update submodules
2 |
3 | on:
4 | workflow_dispatch: {}
5 | schedule:
6 | - cron: 0 2 * * * # every day at 2 am
7 |
8 | jobs:
9 | pull:
10 | runs-on: ubuntu-latest
11 | permissions:
12 | contents: write
13 | steps:
14 | - uses: ConorMacBride/install-package@v1
15 | with:
16 | apt: moreutils
17 | - uses: actions/checkout@v4
18 | - uses: extractions/setup-just@v3
19 |
20 | - run: just check-for-hyprland-updates
21 |
22 | - name: Check if there are changes
23 | id: changes
24 | uses: UnicornGlobal/has-changes-action@v1.0.12
25 |
26 | - if: steps.changes.outputs.changed == 1
27 | name: Commit changes
28 | uses: stefanzweifel/git-auto-commit-action@v5
29 | with:
30 | commit_message: 🍱 Update parser data from wiki
31 |
32 | - if: steps.changes.outputs.changed == 1
33 | name: Bump version and push tag
34 | id: tag_version
35 | uses: miguelfito/github-bump-and-tag-action@v1
36 | with:
37 | github_token: ${{ secrets.GITHUB_TOKEN }}
38 | default_bump: minor
39 |
--------------------------------------------------------------------------------
/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.toptal.com/developers/gitignore/api/go
2 | # Edit at https://www.toptal.com/developers/gitignore?templates=go
3 |
4 | ### Go ###
5 | # If you prefer the allow list template instead of the deny list, see community template:
6 | # https://github.com/github/gitignore/blob/main/community/Golang/Go.AllowList.gitignore
7 | #
8 | # Binaries for programs and plugins
9 | *.exe
10 | *.exe~
11 | *.dll
12 | *.so
13 | *.dylib
14 |
15 | # Test binary, built with `go test -c`
16 | *.test
17 |
18 | # Output of the go coverage tool, specifically when used with LiteIDE
19 | *.out
20 |
21 | # Dependency directories (remove the comment below to include it)
22 | # vendor/
23 |
24 | # Go workspace file
25 | go.work
26 |
27 | # End of https://www.toptal.com/developers/gitignore/api/go
28 | hyprlang-lsp
29 | parser/data/generate/generator
30 | logs
31 | *.vsix
32 | hyprls
33 | hyprls
34 | result/
35 |
--------------------------------------------------------------------------------
/.gitmodules:
--------------------------------------------------------------------------------
1 | [submodule "hyprland-wiki"]
2 | path = hyprland-wiki
3 | url = https://github.com/hyprwm/hyprland-wiki
4 |
--------------------------------------------------------------------------------
/.ortfo/description.md:
--------------------------------------------------------------------------------
1 | ---
2 | started: "2024-04-25"
3 | made with:
4 | - go
5 | - hyprland
6 | tags:
7 | - language
8 | - programming
9 | aliases: [hyprlang-lsp]
10 | layout:
11 | - [m1, p1]
12 | - [m2, l1]
13 | - [m3, m4, m5]
14 | thumbnail: ../demo-completion.png
15 | ---
16 |
17 | # HyprLS
18 |
19 | :: en
20 |
21 | A [LSP](https://en.wikipedia.org/wiki/Language_Server_Protocol) server for [Hyprland](https://hyprland.org)'s configuration language
22 |
23 | [Source code](https://github.com/ewen-lbh/hyprlang-lsp.git)
24 |
25 | 
26 |
27 | 
28 |
29 | 
30 |
31 | 
32 |
33 | 
34 |
35 |
36 | :: fr
37 |
38 | Un serveur [LSP](https://en.wikipedia.org/wiki/Language_Server_Protocol) pour le langage de configuration de [Hyprland](https://hyprland.org)
39 |
40 | [Code source](https://github.com/ewen-lbh/hyprlang-lsp.git)
41 |
42 | 
43 |
44 | 
45 |
46 | 
47 |
48 | 
49 |
50 | 
51 |
--------------------------------------------------------------------------------
/.vscode/extensions.json:
--------------------------------------------------------------------------------
1 | {
2 | // See https://go.microsoft.com/fwlink/?LinkId=827846 to learn about workspace recommendations.
3 | // Extension identifier format: ${publisher}.${name}. Example: vscode.csharp
4 |
5 | // List of extensions which should be recommended for users of this workspace.
6 | "recommendations": [
7 | "dbaeumer.vscode-eslint"
8 | ]
9 | }
--------------------------------------------------------------------------------
/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | // A launch configuration that compiles the extension and then opens it inside a new window
2 | {
3 | "version": "0.2.0",
4 | "configurations": [
5 | {
6 | "type": "extensionHost",
7 | "request": "launch",
8 | "name": "Launch Client",
9 | "runtimeExecutable": "${execPath}",
10 | "cwd": "${workspaceFolder}/vscode",
11 | "args": ["--extensionDevelopmentPath=${workspaceRoot}"],
12 | "outFiles": ["${workspaceRoot}/out/**/*.js"],
13 | "autoAttachChildProcesses": true,
14 | "preLaunchTask": "bun:watch"
15 | },
16 | {
17 | "name": "Language Server E2E Test",
18 | "type": "extensionHost",
19 | "request": "launch",
20 | "runtimeExecutable": "${execPath}",
21 | "args": [
22 | "--extensionDevelopmentPath=${workspaceRoot}/vscode",
23 | "--extensionTestsPath=${workspaceRoot}/out/test/index",
24 | "${workspaceRoot}/testFixture"
25 | ],
26 | "outFiles": ["${workspaceRoot}/vscode/out/test/**/*.js"]
27 | }
28 | ]
29 | }
30 |
--------------------------------------------------------------------------------
/.vscode/settings.json:
--------------------------------------------------------------------------------
1 | {
2 | "editor.insertSpaces": false,
3 | "typescript.tsc.autoDetect": "off",
4 | "typescript.preferences.quoteStyle": "single",
5 | "editor.codeActionsOnSave": {
6 | "source.fixAll.eslint": "explicit"
7 | }
8 | }
--------------------------------------------------------------------------------
/.vscode/tasks.json:
--------------------------------------------------------------------------------
1 | {
2 | "version": "2.0.0",
3 | "tasks": [
4 | {
5 | "type": "bun",
6 | "script": "compile",
7 | "group": "build",
8 | "presentation": {
9 | "panel": "dedicated",
10 | "reveal": "never"
11 | },
12 | "problemMatcher": ["$tsc"]
13 | },
14 | {
15 | "label": "bun:watch",
16 | "type": "bun",
17 | "options": {
18 | "cwd": "${workspaceFolder}/vscode"
19 | },
20 | "script": "watch",
21 | "isBackground": true,
22 | "group": {
23 | "kind": "build",
24 | "isDefault": true
25 | },
26 | "presentation": {
27 | "panel": "dedicated",
28 | "reveal": "never"
29 | },
30 | "problemMatcher": ["$tsc-watch"],
31 | "dependsOn": ["go:compile"]
32 | },
33 | {
34 | "label": "go:compile",
35 | "type": "shell",
36 | "command": "just",
37 | "args": ["build-debug"]
38 | }
39 | ]
40 | }
41 |
--------------------------------------------------------------------------------
/CONTRIBUTING.md:
--------------------------------------------------------------------------------
1 | # Contributing
2 |
3 | Thanks for your interest in contributing to HyprLS :3!
4 |
5 | ## Setup your environment development
6 |
7 | You just need to install:
8 |
9 | - [Go](https://golang.org/doc/install)
10 | - [Just](https://just.systems)
11 |
12 | Then:
13 |
14 | ```sh
15 | git clone --recurse-submodules https://github.com/your/fork
16 | cd fork
17 | just build
18 | ```
19 |
20 | Then, you can build a binary locally with `just build`.
21 | To create a "debug build" that logs all the requests, responses and server logs to files (useful for debugging), you can run `just build-debug`.
22 |
23 | The debug binary is named `hyprlang-lsp` and the regular binary is named `hyprls`.
24 |
25 | ### VSCode
26 |
27 | To develop the vscode extension, you'll also need:
28 |
29 | - [VSCode](https://code.visualstudio.com/), really useful for debugging the extension, you just have to launch from the "start/debug" menu in the sidebar
30 | - [Bun](https://bun.sh)
31 |
32 | Then:
33 |
34 | ```sh
35 | cd vscode
36 | bun i
37 | ```
38 |
39 | > **Note:** "Reloading" does not re-build the Go server, you'll need to kill the "Develoment Host Extension" window and kill the terminal that ran the `go:compile` task before launching again.
40 |
41 | The VSCode dev environment is set up to use the debug binary in development. The path is absolute and hardcoded, so you may need to change it to where your debug binary is (check `vscode/src/extension.ts`).
42 |
43 | ## Implementing a new LSP feature
44 |
45 | 1. Make the server signal that it supports said feature in the `initialize` method in `handler.go`
46 | 2. Add a new file for the feature in the root directory (e.g. `formatting.go`)
47 | 3. Cut the corresponding method(s) from `unimplemented.go` and paste them in the new file
48 |
49 | Read on for more details on the file structure.
50 |
51 | ## File structure
52 |
53 | - `vscode/`: source code for the VSCode client extension
54 | - `cmd/hyprls/main.go`: source code for the executable binary. should contain _very little_ code, just enough to start the server
55 | - `handler.go`: defines the `Handler` struct, which is responsible for handling all the LSP requests. Also defines initialization code.
56 | - `color.go`, `completion.go`, `hover.go`, `symbols.go`: code for the different LSP features
57 | - `state.go`: in-memory map of the opened files' contents as well as a few functions to get things like the current section we are in, the current line, etc.
58 | - `sync.go`: code for `did*` events (when the client signals that content was changed or that files were opened or closed). responsible for maintaining `state.go`'s data up-to-date
59 | - `unimplemented.go`: stub functions for the LSP features that are not yet implemented
60 | - `parser/`: source code for the parser:
61 | - `lowlevel.go`: the low-level parser, which reads the raw data from the server and converts it to sections, that contain:
62 | - assignments: setting a [variable](https://wiki.hyprland.org/Configuring/Variables)
63 | - statements: stuff like `exec-once`, `bind`, etc (see [keywords](https://wiki.hyprland.org/Configuring/Keywords))
64 | - sub-sections: sections nested within that section
65 | - `highlevel.go`: the high-level parser, which reads the sections and converts them to a more structured format. The file is generated by `parser/data/generate/main.go` from the wiki pages (continue reading for more information)
66 | - `decode.go`: transform the representation from the low-level parser to the high-level parser (WIP)
67 | - `data/`: code responsible for storing and getting all the config data: all the valid variable names, their types and descriptions, all valid keywords, etc.
68 | - `keywords.go`: all valid keywords with data to allow getting their documentation from wiki pages
69 | - `sections.go`: code related to sections, mostly used by `parser/data/generate` to create the Go struct definitions for the high-level parser
70 | - `variables.go`: same as `sections.go`, but for the different variables
71 | - `load.go`: code to actually load all the data from the wiki pages. declares a few variables that embed the wiki pages' contents from `parser/data/sources`, then, in an `init()` function (which is run at the start of the program), it loads all the data from the wiki pages into the variables:
72 | 1. Convert the markdown content to HTML
73 | 2. Parse that HTML
74 | 3. Walk through it, extracting data from tables and headings
75 | 4. Store that data in `Section` and `Keywords`
76 | - `sources/`: contains the wiki pages' markdown content. Copied `hyprland-wiki/pages/Configuring/*.md` to here when running `just build`
77 | - `generate/`: code to generate the `highlevel.go` file from the wiki pages. Leverages the data loaded by `load.go` to generate the Go struct definitions for the high-level parser, and also output `ast.json` for debugging purposes (and later maybe to use _that_ instead of converting markdown to HTML and parsing every time the server starts, see #1)
78 |
79 | ## Commit names
80 |
81 | We use the [gitmoji](https://gitmoji.dev/) convention for commit names.
82 |
--------------------------------------------------------------------------------
/Justfile:
--------------------------------------------------------------------------------
1 | serverLogsFilepath := `realpath ./logs/server.log || echo ./logs/server.log`
2 | latestTag := `git describe --tags --abbrev=0 || echo commit:$(git rev-parse --short HEAD)`
3 | latestVersion := `git describe --tags --abbrev=0 | sed 's/v//' || echo commit:$(git rev-parse --short HEAD)`
4 | # Parse content of https://raw.githubusercontent.com/hyprwm/hyprland-wiki/main/pages/version-selector.md to get latest documented version
5 |
6 | check-for-hyprland-updates:
7 | #!/bin/env bash
8 | set -euxo pipefail
9 | latestHyprlandVersion=`curl -s https://raw.githubusercontent.com/hyprwm/hyprland-wiki/main/pages/version-selector.md | grep -oP 'v\d+\.\d+\.\d+' | head -n 1 | sed 's/v//'`
10 | touch hyprland_version
11 | if [ "$(cat hyprland_version)" != "$latestHyprlandVersion" ]; then
12 | echo New version $latestHyprlandVersion released!!! update time :3
13 | echo "$latestHyprlandVersion" > hyprland_version
14 | just pull-wiki
15 | just parser-data
16 | else
17 | echo Nyathing to update
18 | fi
19 |
20 | release tag:
21 | jq '.version = "{{ tag }}"' < vscode/package.json | sponge vscode/package.json
22 | sed -i "s/$(grep 'version' default.nix)/ version = \"{{ tag }}\";/" default.nix
23 | git add vscode/package.json default.nix
24 | git commit -m "🏷️ Release {{ tag }}"
25 | git tag -- v{{ tag }}
26 | cd vscode; bun vsce package; bun vsce publish
27 | git push
28 | git push --tags
29 |
30 | run:
31 | just build
32 | ./hyprlang-lsp
33 |
34 | build:
35 | mkdir -p logs
36 | touch logs/server.log
37 | mkdir -p parser/data/sources
38 | # cp hyprland-wiki/pages/Configuring/*.md parser/data/sources/
39 | go mod tidy
40 | go build -ldflags "-X main.HyprlandWikiVersion=$(cat hyprland_version) -X main.HyprlsVersion={{ latestVersion }}" -o hyprls cmd/hyprls/main.go
41 |
42 | build-debug:
43 | mkdir -p parser/data/sources
44 | cp hyprland-wiki/pages/Configuring/*.md parser/data/sources/
45 | go mod tidy
46 | go build -ldflags "-X main.OutputServerLogs={{ serverLogsFilepath }}" -o hyprlang-lsp cmd/hyprls/main.go
47 |
48 | install:
49 | just build
50 | mkdir -p ~/.local/bin
51 | cp hyprls ~/.local/bin/hyprls
52 |
53 | pull-wiki:
54 | #!/bin/bash
55 | git submodule update --init --recursive --remote
56 | cd hyprland-wiki
57 | hash=$(git log --all --oneline --grep="versions: add $(cat ../hyprland_version)" | cut -d' ' -f1)
58 | echo Using wiki https://github.com/hyprwm/hyprland-wiki/commit/$hash
59 | git checkout $hash
60 | cd ..
61 | cp hyprland-wiki/pages/Configuring/*.md parser/data/sources/
62 |
63 | parser-data:
64 | #!/bin/bash
65 | set -euxo pipefail
66 | just build
67 | cd parser/data/generate
68 | go build -o generator main.go
69 | ./generator > ../../highlevel.go ast.json
70 | gofmt -s -w ../../highlevel.go
71 | jq . < ast.json | sponge ast.json
72 |
73 | update-nix-inputs:
74 | nix flake update
75 |
--------------------------------------------------------------------------------
/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice (including the next
11 | paragraph) shall be included in all copies or substantial portions of the
12 | Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
17 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 | OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------
1 | # HyprLS
2 |
3 |
4 |
5 |
6 | |
7 | |
8 |
9 |
10 | |
11 | |
12 | |
13 |
14 |
15 | A LSP server for Hyprland configuration files.
16 |
17 | ## Features
18 |
19 | Not checked means planned / work in progress.
20 |
21 | - [x] Auto-complete
22 | - [x] Hover
23 | - [ ] TODO: Documentation on hover of categories?
24 | - [x] Go to definition
25 | - [x] Color pickers
26 | - [x] Document symbols
27 | - [ ] Diagnostics
28 | - [ ] Formatting
29 | - [ ] Semantic highlighting
30 |
31 | ## Installation
32 |
33 | ### Linux packages
34 |
35 | hyprls has packages for various distributions, kindly maintained by other people
36 |
37 | [](https://repology.org/project/hyprls/versions)
38 |
39 | ### With `go install`
40 |
41 | ```sh
42 | go install github.com/hyprland-community/hyprls/cmd/hyprls@latest
43 | ```
44 |
45 | ### Pre-built binaries
46 |
47 | Binaries for linux are available in [Releases](https://github.com/hyprland-community/hyprls/releases)
48 |
49 | ### From source
50 |
51 | - Required: [Just](https://just.systems) (`paru -S just` on Arch Linux (btw))
52 |
53 | ```sh
54 | git clone --recurse-submodules https://github.com/hyprland-community/hyprls
55 | cd hyprls
56 | # installs the binary to ~/.local/bin.
57 | # Make sure that directory exists and is in your PATH
58 | just install
59 | ```
60 |
61 | ## Usage
62 |
63 | ### With Neovim
64 |
65 | _Combine with [The tree-sitter grammar for Hyprlang](https://github.com/tree-sitter-grammars/tree-sitter-hyprlang) for syntax highlighting._
66 |
67 | Add this to your `init.lua`:
68 |
69 | ```lua
70 | -- Hyprlang LSP
71 | vim.api.nvim_create_autocmd({'BufEnter', 'BufWinEnter'}, {
72 | pattern = {"*.hl", "hypr*.conf"},
73 | callback = function(event)
74 | print(string.format("starting hyprls for %s", vim.inspect(event)))
75 | vim.lsp.start {
76 | name = "hyprlang",
77 | cmd = {"hyprls"},
78 | root_dir = vim.fn.getcwd(),
79 | }
80 | end
81 | })
82 | ```
83 |
84 | ### VSCode
85 |
86 | #### Official Marketplace (VisualStudio Marketplace)
87 |
88 | Install it [from the marketplace](https://marketplace.visualstudio.com/items?itemName=gwenn°-lbh.vscode-hyprls).
89 |
90 | > [!TIP]
91 | > You can use [the Hyprland extension pack](https://marketplace.visualstudio.com/items?itemName=gwenn°-lbh.hyprland) to also get syntax highlighting.
92 |
93 | #### Open VSX (for VSCodium & others)
94 |
95 | Install it [on OpenVSX](https://open-vsx.org/extension/gwenn°-lbh/vscode-hyprls)
96 |
97 | ### Zed
98 |
99 | Language server support is provided by the [Hyprlang extension](https://zed.dev/extensions?query=hyprlang).
100 | Detailed installation and setup instructions can be found in the [extension repository](https://github.com/WhySoBad/zed-hyprlang-extension) [maintainer = [@WhySoBad](https://github.com/WhySoBad)].
101 |
--------------------------------------------------------------------------------
/cmd/hyprls/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "fmt"
5 | "os"
6 | "path/filepath"
7 |
8 | hyprls "github.com/hyprland-community/hyprls"
9 | "go.uber.org/zap"
10 | )
11 |
12 | var OutputServerLogs string
13 |
14 | func main() {
15 | logconf := zap.NewDevelopmentConfig()
16 | if OutputServerLogs != "" {
17 | logconf.OutputPaths = []string{OutputServerLogs, "stderr"}
18 | }
19 | logger, err := logconf.Build()
20 | if err != nil {
21 | fmt.Printf("while building logger: %s", err)
22 | os.Exit(1)
23 | }
24 |
25 | logger.Debug("going to start server")
26 | if OutputServerLogs != "" {
27 | hyprls.StartServer(logger, filepath.Dir(OutputServerLogs))
28 | } else {
29 | hyprls.StartServer(logger, "")
30 | }
31 | }
32 |
--------------------------------------------------------------------------------
/color.go:
--------------------------------------------------------------------------------
1 | package hyprls
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "math"
7 | "strings"
8 |
9 | "github.com/hyprland-community/hyprls/parser"
10 | "github.com/mazznoer/csscolorparser"
11 | "go.lsp.dev/protocol"
12 | "go.uber.org/zap"
13 | )
14 |
15 | func (h Handler) ColorPresentation(ctx context.Context, params *protocol.ColorPresentationParams) ([]protocol.ColorPresentation, error) {
16 | logger.Debug("LSP:ColorPresentation", zap.Any("color", params.Color), zap.Any("range", params.Range))
17 | return []protocol.ColorPresentation{
18 | {
19 | Label: encodeColorLiteral(params.Color),
20 | TextEdit: &protocol.TextEdit{
21 | Range: params.Range,
22 | NewText: encodeColorLiteral(params.Color),
23 | },
24 | },
25 | }, nil
26 | }
27 |
28 | func (h Handler) DocumentColor(ctx context.Context, params *protocol.DocumentColorParams) ([]protocol.ColorInformation, error) {
29 | document, err := parse(params.TextDocument.URI)
30 | if err != nil {
31 | return []protocol.ColorInformation{}, fmt.Errorf("while parsing: %w", err)
32 | }
33 | colors := make([]protocol.ColorInformation, 0)
34 | document.WalkValues(func(a *parser.Assignment, v *parser.Value) {
35 | if v.Kind == parser.Gradient {
36 | for _, stop := range v.Gradient.Stops {
37 | colors = append(colors, protocol.ColorInformation{
38 | Color: stop.LSPColor(),
39 | Range: stop.LSPRange(),
40 | })
41 | }
42 | return
43 | }
44 |
45 | if v.Kind != parser.Color {
46 | return
47 | }
48 |
49 | colors = append(colors, protocol.ColorInformation{
50 | Color: v.LSPColor(),
51 | Range: v.LSPRange(),
52 | })
53 | })
54 | return colors, nil
55 | }
56 |
57 | func decodeColorLiteral(raw string) protocol.Color {
58 | logger.Debug("decodeColorLiteral", zap.String("raw", raw))
59 | color, err := parser.ParseColor(raw)
60 | if err != nil {
61 | return protocol.Color{}
62 | }
63 |
64 | logger.Debug("decodeColorLiteral", zap.Any("color", color))
65 |
66 | return protocol.Color{
67 | Red: roundToThree(float64(color.R) / 255.0),
68 | Alpha: roundToThree(float64(color.A) / 255.0),
69 | Blue: roundToThree(float64(color.B) / 255.0),
70 | Green: roundToThree(float64(color.G) / 255.0),
71 | }
72 | }
73 |
74 | func encodeColorLiteral(color protocol.Color) string {
75 | logger.Debug("encodeColorLiteral", zap.Any("color", color))
76 | out := strings.TrimPrefix(csscolorparser.Color{
77 | R: roundToThree(color.Red),
78 | G: roundToThree(color.Green),
79 | B: roundToThree(color.Blue),
80 | A: roundToThree(color.Alpha),
81 | }.HexString(), "#")
82 |
83 | if color.Alpha == 1.0 {
84 | out = fmt.Sprintf("rgb(%s)", out)
85 | } else {
86 | out = fmt.Sprintf("rgba(%s)", out)
87 | }
88 |
89 | logger.Debug("encodeColorLiteral", zap.String("out", out))
90 | return out
91 | }
92 |
93 | func roundToThree(f float64) float64 {
94 | return math.Round(f*1_00) / 1_00
95 | }
96 |
--------------------------------------------------------------------------------
/color_test.go:
--------------------------------------------------------------------------------
1 | package hyprls
2 |
3 | import (
4 | "math/rand"
5 | "testing"
6 |
7 | "go.lsp.dev/protocol"
8 | "go.uber.org/zap"
9 | )
10 |
11 | func init() {
12 | if logger == nil {
13 | logger, _ = zap.NewDevelopmentConfig().Build()
14 | }
15 | }
16 |
17 | func TestColorEncoding(t *testing.T) {
18 | for i := 0; i < 20; i++ {
19 | color := randomColor()
20 | colorstring := encodeColorLiteral(color)
21 | decoded := decodeColorLiteral(colorstring)
22 | encoded := encodeColorLiteral(decoded)
23 | if colorstring != encoded {
24 | t.Errorf("Color %d: %s != %s (was decoded to %v)", i, colorstring, encoded, decoded)
25 | }
26 | }
27 | }
28 |
29 | func TestColorDecoding(t *testing.T) {
30 | // red
31 | if !compareColorStructs(protocol.Color{Red: 1, Green: 0, Blue: 0, Alpha: 1}, decodeColorLiteral("rgb(ff0000)")) {
32 | t.Errorf("Color 'red' != {1, 0, 0, 1} (was decoded to %v)", decodeColorLiteral("red"))
33 | }
34 |
35 | // blue
36 | if !compareColorStructs(protocol.Color{Red: 0, Green: 0, Blue: 1, Alpha: 1}, decodeColorLiteral("rgba(0000ffff)")) {
37 | t.Errorf("Color 'blue' != {0, 0, 1, 1} (was decoded to %v)", decodeColorLiteral("blue"))
38 | }
39 |
40 | // green
41 | if !compareColorStructs(protocol.Color{Red: 0, Green: 1, Blue: 0, Alpha: 1}, decodeColorLiteral("0xff00ff00")) {
42 | t.Errorf("Color '0xff00ff00' != {0, 1, 0, 1} (was decoded to %v)", decodeColorLiteral("0xff00ff00"))
43 | }
44 |
45 | for i := 0; i < 20; i++ {
46 | color := randomColor()
47 | originalLogger := *logger
48 | logger = logger.WithOptions(zap.Fields(zap.Int("test color number", i)))
49 | encoded := encodeColorLiteral(color)
50 | decoded := decodeColorLiteral(encoded)
51 | if !compareColorStructs(color, decoded) {
52 | t.Errorf("Color %d: %#v != %#v (was encoded to %s)", i, color, decoded, encoded)
53 | }
54 | logger = &originalLogger
55 | }
56 | }
57 |
58 | func compareColorStructs(a, b protocol.Color) bool {
59 | delta := 0
60 | delta += int(a.Red*255 - b.Red*255)
61 | delta += int(a.Green*255 - b.Green*255)
62 | delta += int(a.Blue*255 - b.Blue*255)
63 | delta += int(a.Alpha*255 - b.Alpha*255)
64 | return delta == 0
65 | }
66 |
67 | func randomColor() protocol.Color {
68 | return protocol.Color{
69 | Red: roundToThree(rand.Float64()),
70 | Green: roundToThree(rand.Float64()),
71 | Blue: roundToThree(rand.Float64()),
72 | Alpha: roundToThree(rand.Float64()),
73 | }
74 | }
75 |
--------------------------------------------------------------------------------
/completion.go:
--------------------------------------------------------------------------------
1 | package hyprls
2 |
3 | import (
4 | "context"
5 | "errors"
6 | "fmt"
7 | "strings"
8 | "unicode"
9 |
10 | "github.com/hyprland-community/hyprls/parser"
11 | parser_data "github.com/hyprland-community/hyprls/parser/data"
12 | "go.lsp.dev/protocol"
13 | "go.uber.org/zap"
14 | )
15 |
16 | func (h Handler) Completion(ctx context.Context, params *protocol.CompletionParams) (*protocol.CompletionList, error) {
17 | line, err := currentLine(params.TextDocument.URI, params.Position)
18 | if err != nil {
19 | return nil, nil
20 | }
21 |
22 | file, err := parse(params.TextDocument.URI)
23 | if err != nil {
24 | return nil, nil
25 | }
26 |
27 | sec := currentSection(file, params.Position)
28 | if sec == nil {
29 | sec = &parser.Section{}
30 | }
31 |
32 | cursorIsAfterEquals := err == nil && strings.Contains(line, "=") && strings.Index(line, "=") < int(params.Position.Character)
33 |
34 | // we are after the equals sign, suggest custom properties only
35 | if cursorIsAfterEquals {
36 | items := make([]protocol.CompletionItem, 0)
37 |
38 | cursorOrLineEnd := min(int(params.Position.Character), len(line)-1)
39 | characterBeforeCursorIsDollarSign := line[cursorOrLineEnd] == '$'
40 |
41 | // Don't propose custom variables if in the middle of typing a word
42 | // Only propose if a dollar sign was typed or is just before the cursor
43 | // Or we are after whitespace
44 | // Or we are in the middle of a color completion (typed a r, and key is a color or gradient)
45 | if !characterBeforeCursorIsDollarSign && !unicode.IsSpace(rune(line[params.Position.Character-1])) {
46 | return nil, nil
47 | }
48 |
49 | // Prevent duplicate dollar signs upon completion accept
50 | var textEditRange protocol.Range
51 | if characterBeforeCursorIsDollarSign {
52 | textEditRange = protocol.Range{
53 | protocol.Position{params.Position.Line, params.Position.Character - 1},
54 | protocol.Position{params.Position.Line, params.Position.Character},
55 | }
56 | } else {
57 | textEditRange = collapsedRange(params.Position)
58 | }
59 |
60 | file.WalkCustomVariables(func(v *parser.CustomVariable) {
61 | items = append(items, protocol.CompletionItem{
62 | Label: "$" + v.Key,
63 | Kind: protocol.CompletionItemKindVariable,
64 | Documentation: protocol.MarkupContent{
65 | Kind: protocol.PlainText,
66 | Value: v.ValueRaw,
67 | },
68 | TextEdit: &protocol.TextEdit{
69 | Range: textEditRange,
70 | NewText: "$" + v.Key,
71 | },
72 | })
73 | })
74 |
75 | textedit := func(t string) *protocol.TextEdit {
76 | return &protocol.TextEdit{
77 | Range: collapsedRange(params.Position),
78 | NewText: t,
79 | }
80 | }
81 |
82 | valueKind := parser.String
83 | if sec != nil {
84 | assignment := currentAssignment(*sec, params.Position)
85 | if assignment != nil {
86 | valueKind, err = parser.ValueKindFromString(assignment.ParserTypeString())
87 | if err != nil {
88 | valueKind = parser.String
89 | }
90 | }
91 | }
92 |
93 | logger.Debug("LSP:Completion", zap.Any("valueKind", valueKind))
94 |
95 | switch valueKind {
96 | case parser.Color, parser.Gradient:
97 |
98 | items = append(items, protocol.CompletionItem{
99 | Label: "rgba(⋯)",
100 | Kind: protocol.CompletionItemKindColor,
101 | InsertTextFormat: protocol.InsertTextFormatSnippet,
102 | Documentation: "Define a color with an alpha channel of the form rgba(RRGGBBAA) in hexadecimal notation.",
103 | TextEdit: textedit("rgba(${0:ffffffff})"),
104 | })
105 |
106 | items = append(items, protocol.CompletionItem{
107 | Label: "rgb(⋯)",
108 | Kind: protocol.CompletionItemKindColor,
109 | InsertTextFormat: protocol.InsertTextFormatSnippet,
110 | TextEdit: textedit("rgb(${0:ffffff})"),
111 | Documentation: "Define a color of the form rgb(RRGGBB) in hexadecimal notation.",
112 | })
113 |
114 | items = append(items, protocol.CompletionItem{
115 | Label: "0xAARRGGBB",
116 | Kind: protocol.CompletionItemKindColor,
117 | InsertTextFormat: protocol.InsertTextFormatSnippet,
118 | TextEdit: textedit("0x${1:ffffffff}"),
119 | Deprecated: true,
120 | Documentation: "Define a color of the form 0xAARRGGBB in hexadecimal notation.",
121 | })
122 | case parser.Bool:
123 | items = append(items, protocol.CompletionItem{
124 | Label: "true",
125 | Kind: protocol.CompletionItemKindValue,
126 | })
127 | items = append(items, protocol.CompletionItem{
128 | Label: "false",
129 | Kind: protocol.CompletionItemKindValue,
130 | })
131 | case parser.Modmask:
132 | for keystring := range parser.ModKeyNames {
133 | items = append(items, protocol.CompletionItem{
134 | Label: keystring,
135 | Kind: protocol.CompletionItemKindEnumMember,
136 | })
137 | }
138 |
139 | }
140 |
141 | return &protocol.CompletionList{
142 | Items: items,
143 | }, nil
144 | }
145 |
146 | availableVariables := make([]parser_data.VariableDefinition, 0)
147 | if sec != nil {
148 | secDef := parser_data.FindSectionDefinitionByName(sec.Name)
149 | if secDef != nil {
150 | availableVariables = append(availableVariables, secDef.Variables...)
151 | }
152 | }
153 |
154 | items := make([]protocol.CompletionItem, 0)
155 | vars:
156 | for _, vardef := range availableVariables {
157 | // Don't suggest variables that are already defined
158 | for _, definedvar := range sec.Assignments {
159 | if vardef.Name == definedvar.Key {
160 | continue vars
161 | }
162 | }
163 |
164 | items = append(items, protocol.CompletionItem{
165 | Label: vardef.Name,
166 | Kind: protocol.CompletionItemKindField,
167 | Documentation: protocol.MarkupContent{
168 | Kind: protocol.Markdown,
169 | Value: fmt.Sprintf("Type: %s\n\n%s", vardef.Type, vardef.Description),
170 | },
171 | })
172 | }
173 |
174 | for _, kw := range parser_data.Keywords {
175 | items = append(items, protocol.CompletionItem{
176 | Label: kw.Name,
177 | Kind: protocol.CompletionItemKindKeyword,
178 | Documentation: protocol.MarkupContent{
179 | Kind: protocol.Markdown,
180 | Value: kw.Description,
181 | },
182 | })
183 | }
184 |
185 | subsections:
186 | for _, subsections := range sec.Subsections {
187 | // Don't suggest subsections that are already defined
188 | for _, definedSubsection := range sec.Subsections {
189 | if subsections.Name == definedSubsection.Name {
190 | continue subsections
191 | }
192 | }
193 |
194 | items = append(items, protocol.CompletionItem{
195 | Label: subsections.Name,
196 | Kind: protocol.CompletionItemKindModule,
197 | Documentation: protocol.MarkupContent{
198 | Kind: protocol.Markdown,
199 | Value: fmt.Sprintf("Subsection of %s", sec.Name),
200 | },
201 | })
202 | }
203 |
204 | return &protocol.CompletionList{
205 | Items: items,
206 | }, nil
207 | }
208 |
209 | func (h Handler) CompletionResolve(ctx context.Context, params *protocol.CompletionItem) (*protocol.CompletionItem, error) {
210 | return nil, errors.New("unimplemented")
211 | }
212 |
--------------------------------------------------------------------------------
/default.nix:
--------------------------------------------------------------------------------
1 | { pkgs ? (
2 | let
3 | inherit (builtins) fetchTree fromJSON readFile;
4 | inherit ((fromJSON (readFile ./flake.lock)).nodes) nixpkgs gomod2nix;
5 | in
6 | import (fetchTree nixpkgs.locked) {
7 | overlays = [
8 | (import "${fetchTree gomod2nix.locked}/overlay.nix")
9 | ];
10 | }
11 | )
12 | , buildGoApplication ? pkgs.buildGoApplication
13 | }:
14 |
15 | buildGoApplication {
16 | pname = "hyprls";
17 | version = "0.7.0";
18 | pwd = ./.;
19 | src = ./.;
20 |
21 | postBuild = ''
22 | rm $GOPATH/bin/generate
23 | '';
24 |
25 | modules = ./gomod2nix.toml;
26 | checkFlags = ["-skip=TestHighLevelParse"]; # not yet implemented
27 | }
28 |
--------------------------------------------------------------------------------
/demo-colors.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyprland-community/hyprls/04c579ad66e7d1e841896fb28ef8b7bfcf67af40/demo-colors.png
--------------------------------------------------------------------------------
/demo-completion.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyprland-community/hyprls/04c579ad66e7d1e841896fb28ef8b7bfcf67af40/demo-completion.png
--------------------------------------------------------------------------------
/demo-hover-keyword.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyprland-community/hyprls/04c579ad66e7d1e841896fb28ef8b7bfcf67af40/demo-hover-keyword.png
--------------------------------------------------------------------------------
/demo-hover.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyprland-community/hyprls/04c579ad66e7d1e841896fb28ef8b7bfcf67af40/demo-hover.png
--------------------------------------------------------------------------------
/demo-symbols.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyprland-community/hyprls/04c579ad66e7d1e841896fb28ef8b7bfcf67af40/demo-symbols.png
--------------------------------------------------------------------------------
/flake.lock:
--------------------------------------------------------------------------------
1 | {
2 | "nodes": {
3 | "flake-utils": {
4 | "inputs": {
5 | "systems": "systems"
6 | },
7 | "locked": {
8 | "lastModified": 1710146030,
9 | "narHash": "sha256-SZ5L6eA7HJ/nmkzGG7/ISclqe6oZdOZTNoesiInkXPQ=",
10 | "owner": "numtide",
11 | "repo": "flake-utils",
12 | "rev": "b1d9ab70662946ef0850d488da1c9019f3a9752a",
13 | "type": "github"
14 | },
15 | "original": {
16 | "owner": "numtide",
17 | "repo": "flake-utils",
18 | "type": "github"
19 | }
20 | },
21 | "gomod2nix": {
22 | "inputs": {
23 | "flake-utils": [
24 | "flake-utils"
25 | ],
26 | "nixpkgs": [
27 | "nixpkgs"
28 | ]
29 | },
30 | "locked": {
31 | "lastModified": 1717050755,
32 | "narHash": "sha256-C9IEHABulv2zEDFA+Bf0E1nmfN4y6MIUe5eM2RCrDC0=",
33 | "owner": "nix-community",
34 | "repo": "gomod2nix",
35 | "rev": "31b6d2e40b36456e792cd6cf50d5a8ddd2fa59a1",
36 | "type": "github"
37 | },
38 | "original": {
39 | "owner": "nix-community",
40 | "repo": "gomod2nix",
41 | "type": "github"
42 | }
43 | },
44 | "nixpkgs": {
45 | "locked": {
46 | "lastModified": 1720957393,
47 | "narHash": "sha256-oedh2RwpjEa+TNxhg5Je9Ch6d3W1NKi7DbRO1ziHemA=",
48 | "owner": "NixOS",
49 | "repo": "nixpkgs",
50 | "rev": "693bc46d169f5af9c992095736e82c3488bf7dbb",
51 | "type": "github"
52 | },
53 | "original": {
54 | "owner": "NixOS",
55 | "ref": "nixos-unstable",
56 | "repo": "nixpkgs",
57 | "type": "github"
58 | }
59 | },
60 | "root": {
61 | "inputs": {
62 | "flake-utils": "flake-utils",
63 | "gomod2nix": "gomod2nix",
64 | "nixpkgs": "nixpkgs"
65 | }
66 | },
67 | "systems": {
68 | "locked": {
69 | "lastModified": 1681028828,
70 | "narHash": "sha256-Vy1rq5AaRuLzOxct8nz4T6wlgyUR7zLU309k9mBC768=",
71 | "owner": "nix-systems",
72 | "repo": "default",
73 | "rev": "da67096a3b9bf56a91d16901293e51ba5b49a27e",
74 | "type": "github"
75 | },
76 | "original": {
77 | "owner": "nix-systems",
78 | "repo": "default",
79 | "type": "github"
80 | }
81 | }
82 | },
83 | "root": "root",
84 | "version": 7
85 | }
86 |
--------------------------------------------------------------------------------
/flake.nix:
--------------------------------------------------------------------------------
1 | {
2 | description = "A LSP server for Hyprland config files";
3 |
4 | inputs = {
5 | nixpkgs.url = "github:NixOS/nixpkgs/nixos-unstable";
6 | flake-utils.url = "github:numtide/flake-utils";
7 | gomod2nix.url = "github:nix-community/gomod2nix";
8 | gomod2nix.inputs.nixpkgs.follows = "nixpkgs";
9 | gomod2nix.inputs.flake-utils.follows = "flake-utils";
10 | };
11 |
12 | outputs = { self, nixpkgs, flake-utils, gomod2nix }:
13 | (flake-utils.lib.eachDefaultSystem
14 | (system:
15 | let
16 | pkgs = nixpkgs.legacyPackages.${system};
17 |
18 | # The current default sdk for macOS fails to compile go projects, so we use a newer one for now.
19 | # This has no effect on other platforms.
20 | callPackage = pkgs.darwin.apple_sdk_11_0.callPackage or pkgs.callPackage;
21 | in
22 | {
23 | packages.default = callPackage ./. {
24 | inherit (gomod2nix.legacyPackages.${system}) buildGoApplication;
25 | };
26 | devShells.default = callPackage ./shell.nix {
27 | inherit (gomod2nix.legacyPackages.${system}) mkGoEnv gomod2nix;
28 | };
29 | })
30 | );
31 | }
32 |
--------------------------------------------------------------------------------
/go.mod:
--------------------------------------------------------------------------------
1 | module github.com/hyprland-community/hyprls
2 |
3 | go 1.24.3
4 |
5 | require (
6 | github.com/MakeNowJust/heredoc v1.0.0
7 | github.com/PuerkitoBio/goquery v1.10.3
8 | github.com/davecgh/go-spew v1.1.1
9 | go.lsp.dev/jsonrpc2 v0.10.0
10 | go.uber.org/multierr v1.11.0
11 | go.uber.org/zap v1.27.0
12 | )
13 |
14 | require (
15 | github.com/andybalholm/cascadia v1.3.3 // indirect
16 | go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 // indirect
17 | go.lsp.dev/uri v0.3.0 // indirect
18 | golang.org/x/net v0.39.0 // indirect
19 | golang.org/x/text v0.24.0 // indirect
20 | )
21 |
22 | require (
23 | github.com/MakeNowJust/heredoc/v2 v2.0.1
24 | github.com/anaskhan96/soup v1.2.5
25 | github.com/evorts/html-to-markdown v0.0.10
26 | github.com/mazznoer/csscolorparser v0.1.6
27 | github.com/metal3d/go-slugify v0.0.0-20160607203414-7ac2014b2f23
28 | github.com/segmentio/asm v1.2.0 // indirect
29 | github.com/segmentio/encoding v0.4.0 // indirect
30 | github.com/yuin/goldmark v1.7.12
31 | go.lsp.dev/protocol v0.12.0
32 | golang.org/x/sys v0.32.0 // indirect
33 | )
34 |
--------------------------------------------------------------------------------
/go.sum:
--------------------------------------------------------------------------------
1 | github.com/MakeNowJust/heredoc v1.0.0 h1:cXCdzVdstXyiTqTvfqk9SDHpKNjxuom+DOlyEeQ4pzQ=
2 | github.com/MakeNowJust/heredoc v1.0.0/go.mod h1:mG5amYoWBHf8vpLOuehzbGGw0EHxpZZ6lCpQ4fNJ8LE=
3 | github.com/MakeNowJust/heredoc/v2 v2.0.1/go.mod h1:6/2Abh5s+hc3g9nbWLe9ObDIOhaRrqsyY9MWy+4JdRM=
4 | github.com/PuerkitoBio/goquery v1.5.1 h1:PSPBGne8NIUWw+/7vFBV+kG2J/5MOjbzc7154OaKCSE=
5 | github.com/PuerkitoBio/goquery v1.5.1/go.mod h1:GsLWisAFVj4WgDibEWF4pvYnkVQBpKBKeU+7zCJoLcc=
6 | github.com/PuerkitoBio/goquery v1.10.3 h1:pFYcNSqHxBD06Fpj/KsbStFRsgRATgnf3LeXiUkhzPo=
7 | github.com/PuerkitoBio/goquery v1.10.3/go.mod h1:tMUX0zDMHXYlAQk6p35XxQMqMweEKB7iK7iLNd4RH4Y=
8 | github.com/anaskhan96/soup v1.2.5 h1:V/FHiusdTrPrdF4iA1YkVxsOpdNcgvqT1hG+YtcZ5hM=
9 | github.com/anaskhan96/soup v1.2.5/go.mod h1:6YnEp9A2yywlYdM4EgDz9NEHclocMepEtku7wg6Cq3s=
10 | github.com/andybalholm/cascadia v1.1.0 h1:BuuO6sSfQNFRu1LppgbD25Hr2vLYW25JvxHs5zzsLTo=
11 | github.com/andybalholm/cascadia v1.1.0/go.mod h1:GsXiBklL0woXo1j/WYWtSYYC4ouU9PqHO0sqidkEA4Y=
12 | github.com/andybalholm/cascadia v1.3.3 h1:AG2YHrzJIm4BZ19iwJ/DAua6Btl3IwJX+VI4kktS1LM=
13 | github.com/andybalholm/cascadia v1.3.3/go.mod h1:xNd9bqTn98Ln4DwST8/nG+H0yuB8Hmgu1YHNnWw0GeA=
14 | github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
15 | github.com/davecgh/go-spew v1.1.1 h1:vj9j/u1bqnvCEfJOwUhtlOARqs3+rkHYY13jYWTU97c=
16 | github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
17 | github.com/evorts/html-to-markdown v0.0.10 h1:8t+OvbvAtooQjAbUH6ekVBxT8DF+t8a5RPPx/lf3NDA=
18 | github.com/evorts/html-to-markdown v0.0.10/go.mod h1:Tng5L77N4uR2zXaoIiIxsQlrQoKPP84rhmEGrOnkBNw=
19 | github.com/google/go-cmp v0.4.0/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
20 | github.com/google/go-cmp v0.5.6 h1:BKbKCqvP6I+rmFHt06ZmyQtvB8xAkWdhFyr0ZUNZcxQ=
21 | github.com/google/go-cmp v0.5.6/go.mod h1:v8dTdLbMG2kIc/vJvl+f65V22dbkXbowE6jgT/gNBxE=
22 | github.com/google/go-cmp v0.6.0/go.mod h1:17dUlkBOakJ0+DkrSSNjCkIjxS6bF9zb3elmeNGIjoY=
23 | github.com/mazznoer/csscolorparser v0.1.6 h1:uK6p5zBA8HaQZJSInHgHVmkVBodUAy+6snSmKJG7pqA=
24 | github.com/mazznoer/csscolorparser v0.1.6/go.mod h1:OQRVvgCyHDCAquR1YWfSwwaDcM0LhnSffGnlbOew/3I=
25 | github.com/metal3d/go-slugify v0.0.0-20160607203414-7ac2014b2f23 h1:UhdgaX0bR9ZSz+jRK6cPQLU94Q3KB14ijuHum8YbvBA=
26 | github.com/metal3d/go-slugify v0.0.0-20160607203414-7ac2014b2f23/go.mod h1:sCALRmIiknhX1lHQ8flRsWKMazu5BBjMochEnDupxrk=
27 | github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
28 | github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
29 | github.com/segmentio/asm v1.2.0 h1:9BQrFxC+YOHJlTlHGkTrFWf59nbL3XnCoFLTwDCI7ys=
30 | github.com/segmentio/asm v1.2.0/go.mod h1:BqMnlJP91P8d+4ibuonYZw9mfnzI9HfxselHZr5aAcs=
31 | github.com/segmentio/encoding v0.4.0 h1:MEBYvRqiUB2nfR2criEXWqwdY6HJOUrCn5hboVOVmy8=
32 | github.com/segmentio/encoding v0.4.0/go.mod h1:/d03Cd8PoaDeceuhUUUQWjU0KhWjrmYrWPgtJHYZSnI=
33 | github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
34 | github.com/stretchr/testify v1.6.1/go.mod h1:6Fq8oRcR53rry900zMqJjRRixrwX3KX962/h/Wwjteg=
35 | github.com/stretchr/testify v1.8.1 h1:w7B6lhMri9wdJUVmEZPGGhZzrYTPvgJArz7wNPgYKsk=
36 | github.com/stretchr/testify v1.8.1/go.mod h1:w2LPCIKwWwSfY2zedu0+kehJoqGctiVI29o6fzry7u4=
37 | github.com/yuin/goldmark v1.4.13/go.mod h1:6yULJ656Px+3vBD8DxQVa3kxgyrAnzto9xy5taEt/CY=
38 | github.com/yuin/goldmark v1.7.12 h1:YwGP/rrea2/CnCtUHgjuolG/PnMxdQtPMO5PvaE2/nY=
39 | github.com/yuin/goldmark v1.7.12/go.mod h1:ip/1k0VRfGynBgxOz0yCqHrbZXhcjxyuS66Brc7iBKg=
40 | go.lsp.dev/jsonrpc2 v0.10.0 h1:Pr/YcXJoEOTMc/b6OTmcR1DPJ3mSWl/SWiU1Cct6VmI=
41 | go.lsp.dev/jsonrpc2 v0.10.0/go.mod h1:fmEzIdXPi/rf6d4uFcayi8HpFP1nBF99ERP1htC72Ac=
42 | go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2 h1:hCzQgh6UcwbKgNSRurYWSqh8MufqRRPODRBblutn4TE=
43 | go.lsp.dev/pkg v0.0.0-20210717090340-384b27a52fb2/go.mod h1:gtSHRuYfbCT0qnbLnovpie/WEmqyJ7T4n6VXiFMBtcw=
44 | go.lsp.dev/protocol v0.12.0 h1:tNprUI9klQW5FAFVM4Sa+AbPFuVQByWhP1ttNUAjIWg=
45 | go.lsp.dev/protocol v0.12.0/go.mod h1:Qb11/HgZQ72qQbeyPfJbu3hZBH23s1sr4st8czGeDMQ=
46 | go.lsp.dev/uri v0.3.0 h1:KcZJmh6nFIBeJzTugn5JTU6OOyG0lDOo3R9KwTxTYbo=
47 | go.lsp.dev/uri v0.3.0/go.mod h1:P5sbO1IQR+qySTWOCnhnK7phBx+W3zbLqSMDJNTw88I=
48 | go.uber.org/goleak v1.3.0 h1:2K3zAYmnTNqV73imy9J1T3WC+gmCePx2hEGkimedGto=
49 | go.uber.org/goleak v1.3.0/go.mod h1:CoHD4mav9JJNrW/WLlf7HGZPjdw8EucARQHekz1X6bE=
50 | go.uber.org/multierr v1.11.0 h1:blXXJkSxSSfBVBlC76pxqeO+LN3aDfLQo+309xJstO0=
51 | go.uber.org/multierr v1.11.0/go.mod h1:20+QtiLqy0Nd6FdQB9TLXag12DsQkrbs3htMFfDN80Y=
52 | go.uber.org/zap v1.27.0 h1:aJMhYGrd5QSmlpLMr2MftRKl7t8J8PTZPA732ud/XR8=
53 | go.uber.org/zap v1.27.0/go.mod h1:GB2qFLM7cTU87MWRP2mPIjqfIDnGu+VIO4V/SdhGo2E=
54 | golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
55 | golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc=
56 | golang.org/x/crypto v0.13.0/go.mod h1:y6Z2r+Rw4iayiXXAIxJIDAJ1zMW4yaTpebo8fPOliYc=
57 | golang.org/x/crypto v0.19.0/go.mod h1:Iy9bg/ha4yyC70EfRS8jz+B6ybOBKMaSxLj6P6oBDfU=
58 | golang.org/x/crypto v0.23.0/go.mod h1:CKFgDieR+mRhux2Lsu27y0fO304Db0wZe70UKqHu0v8=
59 | golang.org/x/crypto v0.31.0/go.mod h1:kDsLvtWBEx7MV9tJOj9bnXsPbxwJQ6csT/x4KIN4Ssk=
60 | golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4=
61 | golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
62 | golang.org/x/mod v0.12.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs=
63 | golang.org/x/mod v0.15.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
64 | golang.org/x/mod v0.17.0/go.mod h1:hTbmBsO62+eylJbnUtE2MGJUyE7QWk4xUqPFrRgJ+7c=
65 | golang.org/x/net v0.0.0-20180218175443-cbe0f9307d01/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4=
66 | golang.org/x/net v0.0.0-20190620200207-3b0461eec859/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
67 | golang.org/x/net v0.0.0-20200114155413-6afb5195e5aa/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
68 | golang.org/x/net v0.0.0-20200202094626-16171245cfb2/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
69 | golang.org/x/net v0.0.0-20200320220750-118fecf932d8 h1:1+zQlQqEEhUeStBTi653GZAnAuivZq/2hz+Iz+OP7rg=
70 | golang.org/x/net v0.0.0-20200320220750-118fecf932d8/go.mod h1:z5CRVTTTmAJ677TzLLGU+0bjPO0LkuOLi4/5GtJWs/s=
71 | golang.org/x/net v0.0.0-20210226172049-e18ecbb05110/go.mod h1:m0MpNAwzfU5UDzcl9v0D8zg8gWTRqZa9RBIspLL5mdg=
72 | golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c=
73 | golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs=
74 | golang.org/x/net v0.10.0/go.mod h1:0qNGK6F8kojg2nk9dLZ2mShWaEBan6FAoqfSigmmuDg=
75 | golang.org/x/net v0.15.0/go.mod h1:idbUs1IY1+zTqbi8yxTbhexhEEk5ur9LInksu6HrEpk=
76 | golang.org/x/net v0.21.0/go.mod h1:bIjVDfnllIU7BJ2DNgfnXvpSvtn8VRwhlsaeUTyUS44=
77 | golang.org/x/net v0.25.0/go.mod h1:JkAGAh7GEvH74S6FOH42FLoXpXbE/aqXSrIQjXgsiwM=
78 | golang.org/x/net v0.33.0/go.mod h1:HXLR5J+9DxmrqMwG9qjGCxZ+zKXxBru04zlTvWlWuN4=
79 | golang.org/x/net v0.39.0 h1:ZCu7HMWDxpXpaiKdhzIfaltL9Lp31x/3fCP11bc6/fY=
80 | golang.org/x/net v0.39.0/go.mod h1:X7NRbYVEA+ewNkCNyJ513WmMdQ3BineSwVtN2zD/d+E=
81 | golang.org/x/sync v0.0.0-20190423024810-112230192c58/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
82 | golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
83 | golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM=
84 | golang.org/x/sync v0.3.0/go.mod h1:FU7BRWz2tNW+3quACPkgCx/L+uEAv1htQ0V83Z9Rj+Y=
85 | golang.org/x/sync v0.6.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
86 | golang.org/x/sync v0.7.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
87 | golang.org/x/sync v0.10.0/go.mod h1:Czt+wKu1gCyEFDUtn0jG5QVvpJ6rzVqr5aXyt9drQfk=
88 | golang.org/x/sys v0.0.0-20190215142949-d0b11bdaac8a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY=
89 | golang.org/x/sys v0.0.0-20201119102817-f84b799fce68/go.mod h1:h1NjWce9XRLGQEsW7wpKNCjG9DtNlClVuFLEZdDNbEs=
90 | golang.org/x/sys v0.0.0-20210615035016-665e8c7367d1/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
91 | golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
92 | golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
93 | golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
94 | golang.org/x/sys v0.8.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
95 | golang.org/x/sys v0.12.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
96 | golang.org/x/sys v0.17.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
97 | golang.org/x/sys v0.19.0 h1:q5f1RH2jigJ1MoAWp2KTp3gm5zAGFUTarQZ5U386+4o=
98 | golang.org/x/sys v0.19.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
99 | golang.org/x/sys v0.20.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
100 | golang.org/x/sys v0.28.0/go.mod h1:/VUhepiaJMQUp4+oa/7Zr1D23ma6VTLIYjOOTFZPUcA=
101 | golang.org/x/sys v0.32.0 h1:s77OFDvIQeibCmezSnk/q6iAfkdiQaJi4VzroCFrN20=
102 | golang.org/x/sys v0.32.0/go.mod h1:BJP2sWEmIv4KK5OTEluFJCKSidICx8ciO85XgH3Ak8k=
103 | golang.org/x/telemetry v0.0.0-20240228155512-f48c80bd79b2/go.mod h1:TeRTkGYfJXctD9OcfyVLyj2J3IxLnKwHJR8f4D8a3YE=
104 | golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
105 | golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8=
106 | golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k=
107 | golang.org/x/term v0.8.0/go.mod h1:xPskH00ivmX89bAKVGSKKtLOWNx2+17Eiy94tnKShWo=
108 | golang.org/x/term v0.12.0/go.mod h1:owVbMEjm3cBLCHdkQu9b1opXd4ETQWc3BhuQGKgXgvU=
109 | golang.org/x/term v0.17.0/go.mod h1:lLRBjIVuehSbZlaOtGMbcMncT+aqLLLmKrsjNrUguwk=
110 | golang.org/x/term v0.20.0/go.mod h1:8UkIAJTvZgivsXaD6/pH6U9ecQzZ45awqEOzuCvwpFY=
111 | golang.org/x/term v0.27.0/go.mod h1:iMsnZpn0cago0GOrHO2+Y7u7JPn5AylBrcoWkElMTSM=
112 | golang.org/x/text v0.3.0 h1:g61tztE5qeGQ89tm6NTjjM9VPIm088od1l6aSorWRWg=
113 | golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
114 | golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
115 | golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
116 | golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8=
117 | golang.org/x/text v0.9.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8=
118 | golang.org/x/text v0.13.0/go.mod h1:TvPlkZtksWOMsz7fbANvkp4WM8x/WCo/om8BMLbz+aE=
119 | golang.org/x/text v0.14.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
120 | golang.org/x/text v0.15.0/go.mod h1:18ZOQIKpY8NJVqYksKHtTdi31H5itFRjB5/qKTNYzSU=
121 | golang.org/x/text v0.21.0/go.mod h1:4IBbMaMmOPCJ8SecivzSH54+73PCFmPWxNTLm+vZkEQ=
122 | golang.org/x/text v0.24.0 h1:dd5Bzh4yt5KYA8f9CJHCP4FB4D51c2c6JvN37xJJkJ0=
123 | golang.org/x/text v0.24.0/go.mod h1:L8rBsPeo2pSS+xqN0d5u2ikmjtmoJbDBT1b7nHvFCdU=
124 | golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
125 | golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
126 | golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc=
127 | golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU=
128 | golang.org/x/tools v0.13.0/go.mod h1:HvlwmtVNQAhOuCjW7xxvovg8wbNq7LwfXh/k7wXUl58=
129 | golang.org/x/tools v0.21.1-0.20240508182429-e35e4ccd0d2d/go.mod h1:aiJjzUbINMkxbQROHiO6hDPo2LHcIPhhQsa9DLh0yGk=
130 | golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
131 | golang.org/x/xerrors v0.0.0-20191204190536-9bdfabe68543/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
132 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1 h1:go1bK/D/BFZV2I8cIQd1NKEZ+0owSTG1fDTci4IqFcE=
133 | golang.org/x/xerrors v0.0.0-20200804184101-5ec99f83aff1/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0=
134 | gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
135 | gopkg.in/yaml.v2 v2.2.8/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=
136 | gopkg.in/yaml.v3 v3.0.0-20200313102051-9f266ea9e77c/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
137 | gopkg.in/yaml.v3 v3.0.1 h1:fxVm/GzAzEWqLHuvctI91KS9hhNmmWOoWu0XTYJS7CA=
138 | gopkg.in/yaml.v3 v3.0.1/go.mod h1:K4uyk7z7BCEPqu6E+C64Yfv1cQ7kz7rIZviUmN+EgEM=
139 |
--------------------------------------------------------------------------------
/gomod2nix.toml:
--------------------------------------------------------------------------------
1 | schema = 3
2 |
3 | [mod]
4 | [mod."github.com/MakeNowJust/heredoc"]
5 | version = "v1.0.0"
6 | hash = "sha256-8hKERAVV1Pew84kc9GkW23dcO8uIUx/+tJQLi+oPwqE="
7 | [mod."github.com/PuerkitoBio/goquery"]
8 | version = "v1.5.1"
9 | hash = "sha256-GqxIdhuN1L3enT8pNlWm+rmkdN5uMfF8TfpxhAHhEDQ="
10 | [mod."github.com/anaskhan96/soup"]
11 | version = "v1.2.5"
12 | hash = "sha256-t8yCyK2y7x2qaI/3Yw16q3zVFqu+3acLcPgTr1MIKWg="
13 | [mod."github.com/andybalholm/cascadia"]
14 | version = "v1.1.0"
15 | hash = "sha256-mL40/Q9iXBzzMHwMomqTOpp9rnI/MRsufb5cIU1MMPg="
16 | [mod."github.com/davecgh/go-spew"]
17 | version = "v1.1.1"
18 | hash = "sha256-nhzSUrE1fCkN0+RL04N4h8jWmRFPPPWbCuDc7Ss0akI="
19 | [mod."github.com/evorts/html-to-markdown"]
20 | version = "v0.0.10"
21 | hash = "sha256-JV/v/rbKakJuSUTcOtRabeB0EzZRL+Nw5fYW5DRMxPg="
22 | [mod."github.com/mazznoer/csscolorparser"]
23 | version = "v0.1.3"
24 | hash = "sha256-v8WyeQKL+SkOar4nftTFrvyitVjFPZRoVeuksp8OnMk="
25 | [mod."github.com/metal3d/go-slugify"]
26 | version = "v0.0.0-20160607203414-7ac2014b2f23"
27 | hash = "sha256-vAfoTRKnDwLi7xLmFXP88auA8ZLJ8W6sLIj569hlkTk="
28 | [mod."github.com/segmentio/asm"]
29 | version = "v1.2.0"
30 | hash = "sha256-zbNuKxNrUDUc6IlmRQNuJQzVe5Ol/mqp7srDg9IMMqs="
31 | [mod."github.com/segmentio/encoding"]
32 | version = "v0.4.0"
33 | hash = "sha256-4pWI9eTZRRDP9kO8rG6vbLCtBVVRLtbCJKd0Z2+8JoU="
34 | [mod."github.com/yuin/goldmark"]
35 | version = "v1.7.1"
36 | hash = "sha256-3EUgwoZRRs2jNBWSbB0DGNmfBvx7CeAgEwyUdaRaeR4="
37 | [mod."go.lsp.dev/jsonrpc2"]
38 | version = "v0.10.0"
39 | hash = "sha256-RbRsMYVBLR7ZDHHGMooycrkdbIauMXkQjVOGP7ggSgM="
40 | [mod."go.lsp.dev/pkg"]
41 | version = "v0.0.0-20210717090340-384b27a52fb2"
42 | hash = "sha256-TxS0Iqe1wbIaFe7MWZJRQdgqhKE8i8CggaGSV9zU1Vg="
43 | [mod."go.lsp.dev/protocol"]
44 | version = "v0.12.0"
45 | hash = "sha256-rZgWIQpHHeYpJL8kiHJ21m1BHXAcr+4Xv2Yh9RNw3s4="
46 | [mod."go.lsp.dev/uri"]
47 | version = "v0.3.0"
48 | hash = "sha256-jGP0N7Gf+bql5oJraUo33sXqWg7AKOTj0D8b4paV4dc="
49 | [mod."go.uber.org/multierr"]
50 | version = "v1.11.0"
51 | hash = "sha256-Lb6rHHfR62Ozg2j2JZy3MKOMKdsfzd1IYTR57r3Mhp0="
52 | [mod."go.uber.org/zap"]
53 | version = "v1.27.0"
54 | hash = "sha256-8655KDrulc4Das3VRduO9MjCn8ZYD5WkULjCvruaYsU="
55 | [mod."golang.org/x/net"]
56 | version = "v0.0.0-20200320220750-118fecf932d8"
57 | hash = "sha256-YdLYymyqwWmiBR6QJdgvOq+QbQvmAh2VAk4KnB+GZAQ="
58 | [mod."golang.org/x/sys"]
59 | version = "v0.19.0"
60 | hash = "sha256-cmuL31TYLJmDm/fDnI2Sn0wB88cpdOHV1+urorsJWx4="
61 | [mod."golang.org/x/text"]
62 | version = "v0.3.0"
63 | hash = "sha256-0FFbaxF1ZuAQF3sCcA85e8MO6prFeHint36inija4NY="
64 |
--------------------------------------------------------------------------------
/handler.go:
--------------------------------------------------------------------------------
1 | package hyprls
2 |
3 | import (
4 | "context"
5 |
6 | "go.lsp.dev/protocol"
7 | "go.uber.org/zap"
8 | )
9 |
10 | type Handler struct {
11 | protocol.Server
12 | Logger *zap.Logger
13 | }
14 |
15 | func NewHandler(ctx context.Context, server protocol.Server, logger *zap.Logger) (Handler, context.Context, error) {
16 |
17 | return Handler{
18 | Server: server,
19 | Logger: logger,
20 | }, context.WithValue(ctx, "state", state{}), nil
21 | }
22 |
23 | func (h Handler) Initialize(ctx context.Context, params *protocol.InitializeParams) (*protocol.InitializeResult, error) {
24 | logger = h.Logger
25 | return &protocol.InitializeResult{
26 | Capabilities: protocol.ServerCapabilities{
27 | HoverProvider: true,
28 | DocumentSymbolProvider: true,
29 | ColorProvider: true,
30 | CompletionProvider: &protocol.CompletionOptions{
31 | ResolveProvider: false,
32 | TriggerCharacters: []string{},
33 | },
34 | TextDocumentSync: protocol.TextDocumentSyncOptions{
35 | OpenClose: true,
36 | Change: protocol.TextDocumentSyncKindFull,
37 | },
38 | },
39 | ServerInfo: &protocol.ServerInfo{
40 | Name: "hyprls",
41 | Version: Version,
42 | },
43 | }, nil
44 | }
45 |
46 | func (h Handler) Initialized(ctx context.Context, params *protocol.InitializedParams) error {
47 | return nil
48 | }
49 |
50 | func (h Handler) Shutdown(ctx context.Context) error {
51 | return nil
52 | }
53 |
54 | func (h Handler) Exit(ctx context.Context) error {
55 | return nil
56 | }
57 |
--------------------------------------------------------------------------------
/hover.go:
--------------------------------------------------------------------------------
1 | package hyprls
2 |
3 | import (
4 | "context"
5 | "fmt"
6 | "strings"
7 |
8 | "github.com/MakeNowJust/heredoc"
9 | parser_data "github.com/hyprland-community/hyprls/parser/data"
10 | "go.lsp.dev/protocol"
11 | )
12 |
13 | func (h Handler) Hover(ctx context.Context, params *protocol.HoverParams) (*protocol.Hover, error) {
14 | line, err := currentLine(params.TextDocument.URI, params.Position)
15 | if err != nil {
16 | return nil, fmt.Errorf("while getting current line of file: %w", err)
17 | }
18 |
19 | if !strings.Contains(line, "=") {
20 | return nil, nil
21 | }
22 |
23 | // key is word before the equal sign. [0] is safe since we checked for "=" above
24 | key := strings.TrimSpace(strings.Split(line, "=")[0])
25 |
26 | indexOfFirstNonWhitespace := strings.IndexFunc(line, func(r rune) bool {
27 | return r != ' ' && r != '\t'
28 | })
29 | indexOfLastNonWhitespace := strings.LastIndexFunc(line, func(r rune) bool {
30 | return r != ' ' && r != '\t'
31 | }) + 1
32 |
33 | for _, section := range parser_data.Sections {
34 | if def := section.VariableDefinition(key); def != nil {
35 | return &protocol.Hover{
36 | Contents: protocol.MarkupContent{
37 | Kind: protocol.Markdown,
38 | Value: heredoc.Docf(`### %s: %s (%s)
39 | %s
40 |
41 | - Defaults to: %s
42 | `, strings.Join(section.Path, ":"), def.Name, def.Type, def.Description, def.PrettyDefault()),
43 | },
44 | Range: &protocol.Range{
45 | Start: protocol.Position{
46 | Line: params.Position.Line,
47 | Character: uint32(indexOfFirstNonWhitespace),
48 | },
49 | End: protocol.Position{
50 | Line: params.Position.Line,
51 | Character: uint32(indexOfLastNonWhitespace),
52 | },
53 | },
54 | }, nil
55 | } else if kw, found := parser_data.FindKeyword(key); found {
56 | flagsLine := ""
57 | if len(kw.Flags) > 0 {
58 | flagsLine = fmt.Sprintf("\n- Accepts the following flags: %s\n", strings.Join(kw.Flags, ", "))
59 | }
60 | return &protocol.Hover{
61 | Contents: protocol.MarkupContent{
62 | Kind: protocol.Markdown,
63 | Value: fmt.Sprintf("### %s [[docs]](%s)%s\n%s", kw.Name, kw.DocumentationLink(), flagsLine, kw.Description),
64 | },
65 | Range: &protocol.Range{
66 | Start: protocol.Position{
67 | Line: params.Position.Line,
68 | Character: uint32(indexOfFirstNonWhitespace),
69 | },
70 | End: protocol.Position{
71 | Line: params.Position.Line,
72 | Character: uint32(indexOfLastNonWhitespace),
73 | },
74 | },
75 | }, nil
76 | }
77 | }
78 |
79 | return nil, nil
80 | }
81 |
--------------------------------------------------------------------------------
/hyprland_version:
--------------------------------------------------------------------------------
1 | 0.49.0
2 |
--------------------------------------------------------------------------------
/main.go:
--------------------------------------------------------------------------------
1 | package hyprls
2 |
3 | import (
4 | "context"
5 | "io"
6 | "os"
7 | "path/filepath"
8 |
9 | "go.lsp.dev/jsonrpc2"
10 | "go.lsp.dev/protocol"
11 | "go.uber.org/multierr"
12 | "go.uber.org/zap"
13 | )
14 |
15 | var Version string
16 |
17 | func StartServer(logger *zap.Logger, logClientIn string) {
18 | logger.Debug("starting server")
19 | conn := jsonrpc2.NewConn(jsonrpc2.NewStream(&readWriteCloser{
20 | reader: os.Stdin,
21 | writer: os.Stdout,
22 | logAt: logClientIn,
23 | }))
24 | handler, ctx, err := NewHandler(context.Background(), protocol.ServerDispatcher(conn, logger), logger)
25 | if err != nil {
26 | logger.Sugar().Fatalf("while initializing handler: %w", err)
27 | }
28 |
29 | conn.Go(ctx, protocol.ServerHandler(handler, jsonrpc2.MethodNotFoundHandler))
30 | <-conn.Done()
31 | }
32 |
33 | type readWriteCloser struct {
34 | reader io.ReadCloser
35 | writer io.WriteCloser
36 | logAt string
37 | }
38 |
39 | func (r *readWriteCloser) Read(b []byte) (int, error) {
40 | var f *os.File
41 | if r.logAt != "" {
42 | f, _ = os.OpenFile(filepath.Join(r.logAt, "client-request-from.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
43 | }
44 | n, err := r.reader.Read(b)
45 | if r.logAt != "" {
46 | if err != nil {
47 | f.Write([]byte(err.Error() + "\n"))
48 | } else {
49 | f.Write(b)
50 | }
51 | }
52 | return n, err
53 | }
54 |
55 | func (r *readWriteCloser) Write(b []byte) (int, error) {
56 | if r.logAt != "" {
57 | f, _ := os.OpenFile(filepath.Join(r.logAt, "client-response-to.log"), os.O_APPEND|os.O_CREATE|os.O_WRONLY, 0644)
58 | f.Write(b)
59 | }
60 | return r.writer.Write(b)
61 | }
62 |
63 | func (r *readWriteCloser) Close() error {
64 | return multierr.Append(r.reader.Close(), r.writer.Close())
65 | }
66 |
--------------------------------------------------------------------------------
/parser/data/generate/main.go:
--------------------------------------------------------------------------------
1 | package main
2 |
3 | import (
4 | "encoding/json"
5 | "fmt"
6 | "os"
7 |
8 | "github.com/MakeNowJust/heredoc"
9 | . "github.com/hyprland-community/hyprls/parser/data"
10 | )
11 |
12 | func main() {
13 | rootSections := make([]SectionDefinition, 0)
14 | for _, section := range Sections {
15 | if len(section.Path) == 1 {
16 | rootSections = append(rootSections, section)
17 | }
18 | }
19 |
20 | jsoned, _ := json.Marshal(struct {
21 | Sections []SectionDefinition `json:"sections"`
22 | Keywords []KeywordDefinition `json:"keywords"`
23 | }{
24 | Sections: rootSections,
25 | Keywords: Keywords,
26 | })
27 | os.WriteFile(os.Args[1], jsoned, 0644)
28 |
29 | fmt.Println(heredoc.Doc(`package parser
30 |
31 | import "image/color"
32 |
33 | type Configuration struct {
34 | CustomVariables map[string]string
35 | `))
36 |
37 | for _, section := range rootSections {
38 | fmt.Printf("\t%s %s `json:\"%s\"`\n", section.Name(), section.TypeName(), section.JSONName())
39 | }
40 |
41 | fmt.Println("}\n\n")
42 |
43 | for _, section := range rootSections {
44 | fmt.Println(section.Typedef())
45 | }
46 |
47 | fmt.Println("\n\n")
48 | }
49 |
--------------------------------------------------------------------------------
/parser/data/keywords.go:
--------------------------------------------------------------------------------
1 | package parser_data
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | type KeywordDefinition struct {
9 | Name string
10 | Description string
11 | documentationHeadingSlug string
12 | documentationFile string
13 | Flags []string
14 | }
15 |
16 | func (k KeywordDefinition) DocumentationLink() string {
17 | return fmt.Sprintf("https://wiki.hyprland.org/Configuring/%s/#%s", k.documentationFile, k.documentationHeadingSlug)
18 | }
19 |
20 | var Keywords = []KeywordDefinition{
21 | {
22 | Name: "submap",
23 | documentationHeadingSlug: "submaps",
24 | documentationFile: "Binds",
25 | Flags: []string{},
26 | },
27 | {
28 | Name: "windowrule",
29 | documentationHeadingSlug: "window-rules-v1",
30 | documentationFile: "Window-Rules",
31 | Flags: []string{},
32 | },
33 | {
34 | Name: "windowrulev2",
35 | documentationHeadingSlug: "window-rules-v2",
36 | documentationFile: "Window-Rules",
37 | Flags: []string{},
38 | },
39 | {
40 | Name: "layerrule",
41 | documentationHeadingSlug: "layer-rules",
42 | documentationFile: "Window-Rules",
43 | Flags: []string{},
44 | },
45 | {
46 | Name: "workspace",
47 | documentationHeadingSlug: "workspace-rules",
48 | documentationFile: "Workspace-Rules",
49 | Flags: []string{},
50 | },
51 | {
52 | Name: "animation",
53 | documentationHeadingSlug: "general",
54 | documentationFile: "Animations",
55 | Flags: []string{},
56 | },
57 | {
58 | Name: "bezier",
59 | documentationHeadingSlug: "curves",
60 | documentationFile: "Animations",
61 | Flags: []string{},
62 | },
63 | {
64 | Name: "exec",
65 | documentationHeadingSlug: "executing",
66 | documentationFile: "Keywords",
67 | Flags: []string{},
68 | },
69 | {
70 | Name: "exec-once",
71 | documentationHeadingSlug: "executing",
72 | documentationFile: "Keywords",
73 | Flags: []string{},
74 | },
75 | {
76 | Name: "source",
77 | documentationHeadingSlug: "sourcing-multi-file",
78 | documentationFile: "Keywords",
79 | Flags: []string{},
80 | },
81 | {
82 | Name: "env",
83 | documentationHeadingSlug: "setting-the-environment",
84 | documentationFile: "Keywords",
85 | Flags: []string{"d"},
86 | },
87 | {
88 | Name: "monitor",
89 | documentationHeadingSlug: "general",
90 | documentationFile: "Monitors",
91 | Flags: []string{},
92 | },
93 | {
94 | Name: "bind",
95 | documentationHeadingSlug: "basic",
96 | documentationFile: "Binds",
97 | Flags: []string{"r", "l", "e", "n", "m", "t", "i"},
98 | },
99 | {
100 | Name: "unbind",
101 | documentationHeadingSlug: "unbind",
102 | documentationFile: "Binds",
103 | Flags: []string{},
104 | },
105 | }
106 |
107 | func FindKeyword(key string) (keyword KeywordDefinition, found bool) {
108 | for _, k := range Keywords {
109 | if key == k.Name {
110 | return k, true
111 | }
112 |
113 | if strings.HasPrefix(key, k.Name) {
114 | if len(k.Flags) > 0 {
115 | usedFlags := strings.Split(strings.TrimPrefix(key, k.Name), "")
116 | for _, used := range usedFlags {
117 | flagIsValid := false
118 | for _, available := range k.Flags {
119 | if used == available {
120 | flagIsValid = true
121 | break
122 | }
123 | }
124 | if !flagIsValid {
125 | return KeywordDefinition{}, false
126 | }
127 | }
128 | return k, true
129 | }
130 | }
131 | }
132 | return KeywordDefinition{}, false
133 | }
134 |
--------------------------------------------------------------------------------
/parser/data/keywords_test.go:
--------------------------------------------------------------------------------
1 | package parser_data
2 |
3 | import "testing"
4 |
5 | func TestFindKeyword(t *testing.T) {
6 | k, found := FindKeyword("submap")
7 | if !found {
8 | t.Fatal("submap not found")
9 | }
10 | if k.Name != "submap" {
11 | t.Fatalf("unexpected name: %q", k.Name)
12 | }
13 |
14 | k, found = FindKeyword("binde")
15 | if !found {
16 | t.Fatal("binde not found")
17 | }
18 | if k.Name != "bind" {
19 | t.Fatalf("unexpected name: %q", k.Name)
20 | }
21 |
22 | }
23 |
--------------------------------------------------------------------------------
/parser/data/load.go:
--------------------------------------------------------------------------------
1 | package parser_data
2 |
3 | import (
4 | "bytes"
5 | "embed"
6 | _ "embed"
7 | "fmt"
8 | "os"
9 | "path/filepath"
10 | "regexp"
11 | "strconv"
12 | "strings"
13 |
14 | "github.com/PuerkitoBio/goquery"
15 | "github.com/anaskhan96/soup"
16 | "github.com/metal3d/go-slugify"
17 | "github.com/yuin/goldmark"
18 | "github.com/yuin/goldmark/extension"
19 |
20 | html2markdown "github.com/evorts/html-to-markdown"
21 | )
22 |
23 | var html2md = html2markdown.NewConverter("wiki.hyprlang.org", true, &html2markdown.Options{})
24 | var md = goldmark.New(goldmark.WithExtensions(extension.GFM))
25 |
26 | func debug(msg string, fmtArgs ...any) {
27 | // fmt.Fprintf(os.Stderr, msg, fmtArgs...)
28 | }
29 |
30 | //go:embed sources/Variables.md
31 | var documentationSource []byte
32 |
33 | //go:embed sources/Master-Layout.md
34 | var masterLayoutDocumentationSource []byte
35 |
36 | //go:embed sources/Dwindle-Layout.md
37 | var dwindleLayoutDocumentationSource []byte
38 |
39 | //go:embed sources/*.md
40 | var documentationSources embed.FS
41 |
42 | var Sections = []SectionDefinition{}
43 |
44 | var undocumentedGeneralSectionVariables = []VariableDefinition{
45 | {
46 | Name: "autogenerated",
47 | Description: "Whether this configuration was autogenerated",
48 | Type: "bool",
49 | Default: "1",
50 | },
51 | }
52 |
53 | func (s SectionDefinition) VariableDefinition(name string) *VariableDefinition {
54 | for _, v := range s.Variables {
55 | if v.Name == name {
56 | return &v
57 | }
58 | }
59 | return nil
60 | }
61 |
62 | func init() {
63 | html2md.AddRules(html2markdown.Rule{
64 | Filter: []string{"a"},
65 | Replacement: func(content string, selec *goquery.Selection, options *html2markdown.Options) *string {
66 | href, _ := selec.Attr("href")
67 | if strings.HasPrefix(href, "../") {
68 | href = strings.Replace(href, "../", "https://wiki.hyprland.org/Configuring/", 1)
69 | }
70 | result := fmt.Sprintf("[%s](%s)", content, href)
71 | return html2markdown.String(result)
72 | },
73 | })
74 |
75 | Sections = parseDocumentationMarkdown(documentationSource, 3)
76 | Sections = append(Sections, parseDocumentationMarkdownWithRootSectionName(masterLayoutDocumentationSource, 2, "Master")...)
77 | Sections = append(Sections, parseDocumentationMarkdownWithRootSectionName(dwindleLayoutDocumentationSource, 2, "Dwindle")...)
78 | addVariableDefsOnSection("General", undocumentedGeneralSectionVariables)
79 |
80 | for i, kw := range Keywords {
81 | if kw.Description != "" {
82 | continue
83 | }
84 |
85 | content, err := documentationSources.ReadFile(filepath.Join("sources", kw.documentationFile+".md"))
86 | if err != nil {
87 | fmt.Fprintf(os.Stderr, "Failed to read documentation file for %s: %s\n", kw.Name, err)
88 | continue
89 | }
90 |
91 | document := markdownToHTML(content)
92 | headings := make([]soup.Root, 0)
93 | for _, t := range []string{"h1", "h2", "h3", "h4", "h5", "h6"} {
94 | headings = append(headings, document.FindAll(t)...)
95 | }
96 | var heading soup.Root
97 | found := false
98 | for _, h := range headings {
99 | if id, ok := h.Attrs()["id"]; ok && id == kw.documentationHeadingSlug {
100 | heading = h
101 | found = true
102 | break
103 | }
104 | anchor := slugify.Marshal(strings.TrimSpace(h.Text()), true)
105 | anchor = regexp.MustCompile(`^weight-%d+-title-`).ReplaceAllString(anchor, "")
106 | if anchor == kw.documentationHeadingSlug {
107 | heading = h
108 | found = true
109 | break
110 | }
111 | }
112 | if !found {
113 | fmt.Fprintf(os.Stderr, "Failed to find heading %s in %s\n", kw.documentationHeadingSlug, kw.documentationFile)
114 | continue
115 | }
116 | Keywords[i].Description, _ = html2md.ConvertString(htmlBetweenHeadingAndNextHeading(heading, heading))
117 | }
118 | }
119 |
120 | func addVariableDefsOnSection(sectionName string, variables []VariableDefinition) {
121 | for i, sec := range Sections {
122 | if sec.Name() != sectionName {
123 | continue
124 | }
125 | Sections[i].Variables = append(Sections[i].Variables, variables...)
126 | }
127 | }
128 |
129 | func parseDocumentationMarkdownWithRootSectionName(source []byte, headingRootLevel int, rootSectionName string) []SectionDefinition {
130 | sections := parseDocumentationMarkdown(source, headingRootLevel)
131 | for i := range sections {
132 | sections[i].Path[0] = rootSectionName
133 | }
134 | return sections
135 | }
136 |
137 | func markdownToHTML(source []byte) soup.Root {
138 | var html bytes.Buffer
139 | err := md.Convert(source, &html)
140 | if err != nil {
141 | panic(err)
142 | }
143 |
144 | return soup.HTMLParse(html.String())
145 | }
146 |
147 | func parseDocumentationMarkdown(source []byte, headingRootLevel int) (sections []SectionDefinition) {
148 | document := markdownToHTML(source)
149 | for _, table := range document.FindAll("table") {
150 | if !arraysEqual(tableHeaderCells(table), []string{"name", "description", "type", "default"}) {
151 | continue
152 | }
153 |
154 | // fmt.Printf("Processing table %s\n", table.HTML())
155 | section := SectionDefinition{
156 | Path: tablePath(table, headingRootLevel),
157 | }
158 | section.Variables = make([]VariableDefinition, 0)
159 | for _, row := range table.FindAll("tr")[1:] {
160 | cells := row.FindAll("td")
161 | if len(cells) != 4 {
162 | continue
163 | }
164 |
165 | section.Variables = append(section.Variables, VariableDefinition{
166 | Name: cells[0].FullText(),
167 | Description: cells[1].FullText(),
168 | Type: cells[2].FullText(),
169 | Default: cells[3].FullText()})
170 | }
171 | sections = append(sections, section)
172 | }
173 |
174 | for i, section := range sections {
175 | if len(section.Path) == 1 {
176 | sections[i] = section.AttachSubsections(sections)
177 | }
178 | }
179 | return sections
180 | }
181 |
182 | func (s SectionDefinition) AttachSubsections(sections []SectionDefinition) SectionDefinition {
183 | // TODO make it work for recursively nested sections
184 | s.Subsections = make([]SectionDefinition, 0)
185 | for _, section := range sections {
186 | if len(section.Path) == 1 {
187 | continue
188 | }
189 | if section.Path[0] == s.Name() {
190 | debug("adding %s to %s\n", section.Name(), s.Name())
191 | s.Subsections = append(s.Subsections, section)
192 | }
193 | }
194 | return s
195 | }
196 |
197 | func tableHeaderCells(table soup.Root) []string {
198 | headerCells := table.FindAll("th")
199 | cells := make([]string, 0, len(headerCells))
200 | for _, cell := range headerCells {
201 | cells = append(cells, cell.FullText())
202 | }
203 | return cells
204 | }
205 |
206 | func tablePath(table soup.Root, headingRootLevel int) []string {
207 | header := backtrackToNearestHeader(table)
208 | level, err := strconv.Atoi(header.NodeValue[1:])
209 | if err != nil {
210 | panic(err)
211 | }
212 | if level <= headingRootLevel {
213 | return []string{header.FullText()}
214 | }
215 | return append(tablePath(header.FindPrevElementSibling(), headingRootLevel), header.FullText())
216 | }
217 |
218 | func backtrackToNearestHeader(element soup.Root) soup.Root {
219 | if element.NodeValue != "table" {
220 | debug("backtracking to nearest header from %s\n", element.HTML())
221 | }
222 | if regexp.MustCompile(`^h[1-6]$`).MatchString(element.NodeValue) {
223 | debug("-> returning from backtrack with %s\n", element.HTML())
224 | return element
225 | }
226 | prev := element.FindPrevElementSibling()
227 | debug("-> prev is %s\n", prev.HTML())
228 | return backtrackToNearestHeader(prev)
229 | }
230 |
231 | func htmlBetweenHeadingAndNextHeading(heading soup.Root, element soup.Root) string {
232 | next := element.FindNextElementSibling()
233 | if isHeading(next) && headingLevel(next) == headingLevel(heading) {
234 | return ""
235 | }
236 |
237 | defer func() {
238 | if crash := recover(); crash != nil {
239 | if os.Getenv("DEBUG") != "" {
240 | fmt.Fprintf(os.Stderr, "Panic while rendering %s\n", next.HTML())
241 | }
242 | }
243 | }()
244 |
245 | rendered := next.HTML()
246 |
247 | return rendered + htmlBetweenHeadingAndNextHeading(heading, next)
248 | }
249 |
250 | func isHeading(element soup.Root) bool {
251 | return regexp.MustCompile(`^h[1-6]$`).MatchString(element.NodeValue)
252 | }
253 |
254 | func headingLevel(heading soup.Root) int {
255 | level, err := strconv.Atoi(heading.NodeValue[1:])
256 | if err != nil {
257 | panic(err)
258 | }
259 | return level
260 | }
261 |
262 | func arraysEqual(a, b []string) bool {
263 | if len(a) != len(b) {
264 | return false
265 | }
266 | for i, v := range a {
267 | if strings.TrimSpace(v) != strings.TrimSpace(b[i]) {
268 | return false
269 | }
270 | }
271 | return true
272 | }
273 |
274 | func toPascalCase(s string) string {
275 | out := ""
276 | for _, word := range regexp.MustCompile(`[-_\.]`).Split(s, -1) {
277 | out += strings.ToUpper(word[:1]) + word[1:]
278 | }
279 | return out
280 | }
281 |
--------------------------------------------------------------------------------
/parser/data/sections.go:
--------------------------------------------------------------------------------
1 | package parser_data
2 |
3 | import (
4 | "fmt"
5 | "strings"
6 | )
7 |
8 | func FindSectionDefinitionByName(name string) *SectionDefinition {
9 | for _, sec := range Sections {
10 | if sec.Name() == name || sec.JSONName() == name {
11 | return &sec
12 | }
13 | }
14 | return nil
15 | }
16 |
17 | type SectionDefinition struct {
18 | Path []string
19 | Subsections []SectionDefinition
20 | Variables []VariableDefinition
21 | }
22 |
23 | func (s SectionDefinition) Name() string {
24 | if len(s.Path) == 0 {
25 | return ""
26 | }
27 | return s.Path[len(s.Path)-1]
28 | }
29 |
30 | func (s SectionDefinition) JSONName() string {
31 | return strings.ToLower(s.Name())
32 | }
33 |
34 | func (s SectionDefinition) TypeName() string {
35 | return "Configuration" + toPascalCase(strings.Join(s.Path, "_"))
36 | }
37 |
38 | func (s SectionDefinition) Typedef() string {
39 | out := fmt.Sprintf("type %s struct {\n", s.TypeName())
40 | for _, def := range s.Variables {
41 | out += fmt.Sprintf("\t// %s\n", def.Description)
42 | out += fmt.Sprintf("\t%s %s `json:\"%s\"`\n", def.PascalCaseName(), def.GoType(), def.Name)
43 | out += "\n"
44 | }
45 | for _, sec := range s.Subsections {
46 | out += fmt.Sprintf("\t%s %s `json:\"%s\"`\n", sec.Name(), sec.TypeName(), strings.ToLower(sec.Name()))
47 | }
48 | out += "}\n"
49 | out += "\n"
50 |
51 | for _, sec := range s.Subsections {
52 | out += sec.Typedef()
53 | }
54 | return out
55 | }
56 |
--------------------------------------------------------------------------------
/parser/data/sources/Animations.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 9
3 | title: Animations
4 | ---
5 |
6 | ## General
7 |
8 | Animations are declared with the `animation` keyword.
9 |
10 | ```ini
11 | animation = NAME, ONOFF, SPEED, CURVE [,STYLE]
12 | ```
13 |
14 | `ONOFF` can be either 0 or 1, 0 to disable, 1 to enable. _note:_ if it's 0, you
15 | can omit further args.
16 |
17 | `SPEED` is the amount of ds (1ds = 100ms) the animation will take
18 |
19 | `CURVE` is the bezier curve name, see [curves](#curves).
20 |
21 | `STYLE` (optional) is the animation style
22 |
23 | The animations are a tree. If an animation is unset, it will inherit its
24 | parent's values. See [the animation tree](#animation-tree).
25 |
26 | ### Examples
27 |
28 | ```ini
29 | animation = workspaces, 1, 8, default
30 | animation = windows, 1, 10, myepiccurve, slide
31 | animation = fade, 0
32 | ```
33 |
34 | ### Animation tree
35 |
36 | ```txt
37 | global
38 | ↳ windows - styles: slide, popin, gnomed
39 | ↳ windowsIn - window open - styles: same as windows
40 | ↳ windowsOut - window close - styles: same as windows
41 | ↳ windowsMove - everything in between, moving, dragging, resizing.
42 | ↳ layers - styles: slide, popin, fade
43 | ↳ layersIn - layer open
44 | ↳ layersOut - layer close
45 | ↳ fade
46 | ↳ fadeIn - fade in for window open
47 | ↳ fadeOut - fade out for window close
48 | ↳ fadeSwitch - fade on changing activewindow and its opacity
49 | ↳ fadeShadow - fade on changing activewindow for shadows
50 | ↳ fadeDim - the easing of the dimming of inactive windows
51 | ↳ fadeLayers - for controlling fade on layers
52 | ↳ fadeLayersIn - fade in for layer open
53 | ↳ fadeLayersOut - fade out for layer close
54 | ↳ border - for animating the border's color switch speed
55 | ↳ borderangle - for animating the border's gradient angle - styles: once (default), loop
56 | ↳ workspaces - styles: slide, slidevert, fade, slidefade, slidefadevert
57 | ↳ workspacesIn - styles: same as workspaces
58 | ↳ workspacesOut - styles: same as workspaces
59 | ↳ specialWorkspace - styles: same as workspaces
60 | ↳ specialWorkspaceIn - styles: same as workspaces
61 | ↳ specialWorkspaceOut - styles: same as workspaces
62 | ```
63 |
64 | {{< callout type=warning >}}
65 |
66 | Using borderangle style: loop requires Hyprland to constantly push out new frames at [your monitor's hz] times a second which might put stress on your CPU/GPU and affect battery life. This will be applied even if animation are disabled or borders are not visible.
67 |
68 | {{ callout >}}
69 |
70 | ## Curves
71 |
72 | Defining your own Bezier curve can be done with the `bezier` keyword:
73 |
74 | ```ini
75 | bezier = NAME, X0, Y0, X1, Y1
76 | ```
77 |
78 | where `NAME` is the name, and the rest are two points for the Cubic Bezier. A
79 | good website to design your bezier can be found
80 | [here, on cssportal.com](https://www.cssportal.com/css-cubic-bezier-generator/),
81 | but if you want to instead choose from a list of beziers, you can check out
82 | [easings.net](https://easings.net).
83 |
84 | ### Example
85 |
86 | ```ini
87 | bezier = overshot, 0.05, 0.9, 0.1, 1.1
88 | ```
89 |
90 | ### Extras
91 |
92 | For animation style `popin` in `windows`, you can specify a minimum percentage
93 | to start from. For example, the following will make the animation 80% -> 100% of
94 | the size:
95 |
96 | ```ini
97 | animation = windows, 1, 8, default, popin 80%
98 | ```
99 |
100 | For animation styles `slidefade` and `slidefadevert` in `workspaces`, you can
101 | specify a movement percentage. For example, the following will make windows move
102 | 20% of the screen width:
103 |
104 | ```ini
105 | animation = workspaces, 1, 8, default, slidefade 20%
106 | ```
107 |
108 | For animation style `slide` in windows and layers you can specify a forced side,
109 | e.g.:
110 |
111 | ```ini
112 | animation = windows, 1, 8, default, slide left
113 | ```
114 |
115 | You can use `top`, `bottom`, `left` or `right`.
116 |
--------------------------------------------------------------------------------
/parser/data/sources/Binds.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 5
3 | title: Binds
4 | ---
5 |
6 | ## Basic
7 |
8 | ```ini
9 | bind = MODS, key, dispatcher, params
10 | ```
11 |
12 | for example,
13 |
14 | ```ini
15 | bind = SUPER_SHIFT, Q, exec, firefox
16 | ```
17 |
18 | will bind opening Firefox to SUPER + SHIFT + Q
19 |
20 | {{< callout type=info >}}
21 |
22 | For binding keys without a modkey, leave it empty:
23 |
24 | ```ini
25 | bind = , Print, exec, grim
26 | ```
27 |
28 | {{< /callout >}}
29 |
30 | _For a complete mod list, see [Variables](../Variables/#variable-types)._
31 |
32 | _The dispatcher list can be found in
33 | [Dispatchers](../Dispatchers/#list-of-dispatchers)._
34 |
35 | ## Uncommon syms / binding with a keycode
36 |
37 | See the
38 | [xkbcommon-keysyms.h header](https://github.com/xkbcommon/libxkbcommon/blob/master/include/xkbcommon/xkbcommon-keysyms.h)
39 | for all the keysyms. The name you should use is the segment after `XKB_KEY_`.
40 |
41 | If you are unsure of what your key's name is, you can use `xev` or `wev` to find
42 | that information.
43 |
44 | If you want to bind by a keycode, you can put it in the KEY position with
45 | a `code:` prefix, e.g.:
46 |
47 | ```ini
48 | bind = SUPER, code:28, exec, amongus
49 | ```
50 |
51 | Will bind SUPER + T. (T is keycode 28.) - You
52 | can also use `xev` or `wev` to find keycodes.
53 |
54 | ## Misc
55 |
56 | ### Workspace bindings on non-qwerty layouts
57 |
58 | Keys used for keybinds need to be accessible without any modifiers in your layout. For instance, the `French Azerty` layout uses `SHIFT+unmodified_key` to write `0-9` numbers. As such, the workspace keybinds for this layout need to use the names of the `unmodified_key`s, and will not work when using the `0-9` numbers.
59 |
60 | {{< callout type=info >}}
61 |
62 | To get the correct name for an `unmodified_key`, refer to [the section on uncommon syms](#uncommon-syms--binding-with-a-keycode)
63 |
64 | {{< /callout >}}
65 |
66 | ```ini
67 | # On a french layout, instead of
68 | # bind = $mainMod, 1, workspace, 1
69 |
70 | # Use
71 | bind = $mainMod, ampersand, workspace, 1
72 | ```
73 |
74 | For help configuring the `French Azerty` layout, [see](https://rherault.dev/articles/hyprland-fr-layout).
75 |
76 | ### Unbind
77 |
78 | You can also unbind with `unbind`, e.g.:
79 |
80 | ```ini
81 | unbind = SUPER, O
82 | ```
83 |
84 | May be useful for dynamic keybindings with `hyprctl`:
85 |
86 | ```bash
87 | hyprctl keyword unbind SUPER, O
88 | ```
89 |
90 | ### Mouse buttons
91 |
92 | You can also bind mouse buttons, by prefacing the mouse keycode with `mouse:`,
93 | for example:
94 |
95 | ```ini
96 | bind = SUPER, mouse:272, exec, amongus
97 | ```
98 |
99 | will bind it to SUPER + LMB.
100 |
101 | ### Only modkeys
102 |
103 | For binding only modkeys, you need to use the TARGET modmask (with the
104 | activating mod) and the `r` flag, e.g.:
105 |
106 | ```ini
107 | bindr = SUPER ALT, Alt_L, exec, amongus
108 | ```
109 |
110 | Will bind `exec amongus` to SUPER + ALT
111 |
112 | ### Keysym combos
113 |
114 | For an arbitrary combination of multiple keys, separate keysyms with `&` between
115 | each mod/key and use the `s` flag, e.g.:
116 |
117 | ```ini
118 | # You can use a single mod with multiple keys.
119 | binds = Control_L, A&Z, exec, kitty
120 | # You can also specify multiple specific mods.
121 | binds = Control_L&Shift_L, K, exec, kitty
122 | # You can also do both!
123 | binds = Control_R&Super_R&Alt_L, J&K&L, exec, kitty
124 | # If you are feeling a little wild... you can use other keys for binds...
125 | binds = Escape&Apostrophe&F7, T&O&A&D, exec, battletoads 2: retoaded
126 | ```
127 |
128 | (Please note this is only valid for keysyms and it makes all mods keysyms. If
129 | you don't know what a keysym is use `xev` and press the key you want to use.)
130 |
131 | ### Mouse wheel
132 |
133 | You can also bind the mouse wheel with `mouse_up` and `mouse_down` (or
134 | `mouse_left` and `mouse_right` if your wheel supports horizontal scrolling):
135 |
136 | ```ini
137 | bind = SUPER, mouse_down, workspace, e-1
138 | ```
139 |
140 | (control the reset time with `binds:scroll_event_delay`)
141 |
142 | ### Switches
143 |
144 | Useful for binding e.g. the lid close/open event:
145 |
146 | ```ini
147 | # trigger when the switch is toggled
148 | bindl = , switch:[switch name], exec, swaylock
149 | # trigger when the switch is turning on
150 | bindl = , switch:on:[switch name], exec, hyprctl keyword monitor "eDP-1, disable"
151 | # trigger when the switch is turning off
152 | bindl = , switch:off:[switch name], exec, hyprctl keyword monitor "eDP-1, 2560x1600, 0x0, 1"
153 | ```
154 |
155 | You can view your switches in `hyprctl devices`.
156 |
157 | ### Multiple binds to one key
158 |
159 | You can trigger multiple actions with one keybind by assigning multiple binds to
160 | one combination, e.g.:
161 |
162 | ```ini
163 | # to switch between windows in a floating workspace
164 | bind = SUPER, Tab, cyclenext, # change focus to another window
165 | bind = SUPER, Tab, bringactivetotop, # bring it to the top
166 | ```
167 |
168 | The keybinds will be executed in the order they were created. (top to bottom)
169 |
170 | ### Description
171 |
172 | You can describe your keybind with the description flag.
173 | Your description always goes in front of the dispatcher and should never contain the character `,`!
174 |
175 | ```ini
176 | bindd = MODS, key, description, dispatcher, params
177 | ```
178 |
179 | for example,
180 |
181 | ```ini
182 | bindd = SUPER, Q, Open my favourite terminal, exec, kitty
183 | ```
184 |
185 | If you want to access your description you can use `hyprctl binds`. For more information have a look at [Using Hyprctl](../Using-hyprctl).
186 |
187 | ## Bind flags
188 |
189 | `bind` supports flags in this format:
190 |
191 | ```ini
192 | bind[flags] = ...
193 | ```
194 |
195 | e.g.:
196 |
197 | ```ini
198 | bindrl = MOD, KEY, exec, amongus
199 | ```
200 |
201 | Flags:
202 |
203 | ```plain
204 | l -> locked, will also work when an input inhibitor (e.g. a lockscreen) is active.
205 | r -> release, will trigger on release of a key.
206 | c -> click, will trigger on release of a key or button as long as the mouse cursor stays inside binds:drag_threshold.
207 | g -> drag, will trigger on release of a key or button as long as the mouse cursor moves outside binds:drag_threshold.
208 | o -> longPress, will trigger on long press of a key.
209 | e -> repeat, will repeat when held.
210 | n -> non-consuming, key/mouse events will be passed to the active window in addition to triggering the dispatcher.
211 | m -> mouse, see below.
212 | t -> transparent, cannot be shadowed by other binds.
213 | i -> ignore mods, will ignore modifiers.
214 | s -> separate, will arbitrarily combine keys between each mod/key, see [Keysym combos](#keysym-combos) above.
215 | d -> has description, will allow you to write a description for your bind.
216 | p -> bypasses the app's requests to inhibit keybinds.
217 | ```
218 |
219 | Example Usage:
220 |
221 | ```ini
222 | # Example volume button that allows press and hold, volume limited to 150%
223 | binde = , XF86AudioRaiseVolume, exec, wpctl set-volume -l 1.5 @DEFAULT_AUDIO_SINK@ 5%+
224 |
225 | # Example volume button that will activate even while an input inhibitor is active
226 | bindl = , XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
227 |
228 | # Start wofi opens wofi on first press, closes it on second
229 | bindr = SUPER, SUPER_L, exec, pkill wofi || wofi
230 |
231 | # Describe a bind
232 | bindd = SUPER, Q, Open my favourite terminal, exec, kitty
233 |
234 | # Skip player on long press and only skip 5s on normal press
235 | bindo = SUPER, XF86AudioNext, exec, playerctl next
236 | bind = SUPER, XF86AudioNext, exec, playerctl position +5
237 | ```
238 |
239 | ## Mouse Binds
240 |
241 | Mouse binds are binds that rely on mouse movement. They will have one less arg.
242 | `binds:drag_threshold` can be used to differentiate between clicks and drags with the same button:
243 |
244 | ```ini
245 | binds {
246 | drag_threshold = 10
247 | }
248 | bindm = ALT, mouse:272, movewindow
249 | bindc = ALT, mouse:272, togglefloating
250 | ```
251 |
252 | This will create a bind with ALT + LMB to move the window
253 | by dragging more than 10px or float it by clicking.
254 |
255 | _Available mouse binds_:
256 |
257 | | Name | Description | Params |
258 | | --- | --- | --- |
259 | | movewindow | moves the active window | none |
260 | | resizewindow | resizes the active window | 1 - resize and keep window aspect ratio, 2 - resize and ignore `keepaspectratio` window rule/prop, none or anything else for normal resize |
261 |
262 | _Common mouse buttons' codes:_
263 |
264 | ```txt
265 | LMB -> 272
266 | RMB -> 273
267 | ```
268 |
269 | _for more, you can of course use `wev` to check._
270 |
271 | {{< callout type=info >}}
272 |
273 | Mouse binds, despite their name, behave like normal binds. You are free to use
274 | whatever keys / mods you please. When held, the mouse function will be
275 | activated.
276 |
277 | {{< /callout >}}
278 |
279 | ### Touchpad
280 |
281 | As clicking and moving the mouse on a touchpad is unergonomic, you can also use keyboard keys instead of mouse clicks too.
282 |
283 | ```ini
284 | bindm = SUPER, mouse:272, movewindow
285 | bindm = SUPER, Control_L, movewindow
286 | bindm = SUPER, mouse:273, resizewindow
287 | bindm = SUPER, ALT_L, resizewindow
288 | ```
289 |
290 | ## Binding mods
291 |
292 | You can bind a mod alone like this:
293 |
294 | ```ini
295 | bindr = ALT,Alt_L,exec,amongus
296 | ```
297 |
298 | ## Global Keybinds
299 |
300 | ### Classic
301 |
302 | Yes, you heard this right, Hyprland does support global keybinds for ALL apps,
303 | including OBS, Discord, Firefox, etc.
304 |
305 | See the [`pass` dispatcher](../Dispatchers/#list-of-dispatchers) and the
306 | [`sendshortcut` dispatcher](../Dispatchers/#list-of-dispatchers) for keybinds.
307 |
308 | Let's take OBS as an example: the "Start/Stop Recording" keybind is set to
309 | SUPER + F10, and you want to make it work globally. Simply
310 | add
311 |
312 | ```ini
313 | bind = SUPER, F10, pass, class:^(com\.obsproject\.Studio)$
314 | ```
315 |
316 | to your config and you're done.
317 |
318 | `pass` will pass the PRESS and RELEASE events by itself, no need for a `bindr`.
319 | This also means that push-to-talk will work flawlessly with one pass, e.g.:
320 |
321 | ```ini
322 | bind = , mouse:276, pass, class:^(TeamSpeak 3)$
323 | ```
324 |
325 | Will pass MOUSE5 to TeamSpeak3.
326 |
327 | You may also add shortcuts, where other keys are passed to the window.
328 |
329 | ```ini
330 | bind = SUPER, F10, sendshortcut, SUPER, F4, class:^(com\.obsproject\.Studio)$
331 | ```
332 |
333 | Will send SUPER + F4 to OBS if you press
334 | SUPER + F10.
335 |
336 | {{< callout >}}
337 |
338 | This works flawlessly with all native Wayland applications. However, XWayland
339 | is a bit wonky. Make sure that what you're passing is a "global Xorg keybind",
340 | otherwise passing from a different XWayland app may not work.
341 |
342 | {{< /callout >}}
343 |
344 | ### DBus Global Shortcuts
345 |
346 | Some applications may already support the GlobalShortcuts portal in
347 | xdg-desktop-portal.
348 |
349 | If that's the case, then it's recommended to use this method instead of `pass`.
350 |
351 | Open your desired app and run `hyprctl globalshortcuts` in a terminal. This will
352 | give you a list of currently registered shortcuts with their description(s).
353 |
354 | Choose whichever you like, for example `coolApp:myToggle`, and bind it to
355 | whatever you want with the `global` dispatcher:
356 |
357 | ```ini
358 | bind = SUPERSHIFT, A, global, coolApp:myToggle
359 | ```
360 |
361 | {{< callout type=info >}}
362 |
363 | Please note that this function will _only_ work with
364 | [XDPH](../../Hypr-Ecosystem/xdg-desktop-portal-hyprland).
365 |
366 | {{ callout >}}
367 |
368 | ## Submaps
369 |
370 | Keybind submaps, also known as _modes_ or _groups_, allow you to activate a
371 | separate set of keybinds. For example, if you want to enter a "resize" mode
372 | which allows you to resize windows with the arrow keys, you can do it like this:
373 |
374 | ```ini
375 | # will switch to a submap called resize
376 | bind = ALT, R, submap, resize
377 |
378 | # will start a submap called "resize"
379 | submap = resize
380 |
381 | # sets repeatable binds for resizing the active window
382 | binde = , right, resizeactive, 10 0
383 | binde = , left, resizeactive, -10 0
384 | binde = , up, resizeactive, 0 -10
385 | binde = , down, resizeactive, 0 10
386 |
387 | # use reset to go back to the global submap
388 | bind = , escape, submap, reset
389 |
390 | # will reset the submap, which will return to the global submap
391 | submap = reset
392 |
393 | # keybinds further down will be global again...
394 | ```
395 |
396 | {{< callout type=warning >}}
397 |
398 | Do not forget a keybind to reset the keymap while inside it! (In this case,
399 | `escape`)
400 |
401 | {{< /callout >}}
402 |
403 | If you get stuck inside a keymap, you can use `hyprctl dispatch submap reset` to
404 | go back. If you do not have a terminal open, tough luck buddy. You have been
405 | warned.
406 |
407 | You can also set the same keybind to perform multiple actions, such as resize
408 | and close the submap, like so:
409 |
410 | ```ini
411 | bind = ALT, R, submap, resize
412 |
413 | submap = resize
414 |
415 | bind = , right, resizeactive, 10 0
416 | bind = , right, submap, reset
417 | # ...
418 |
419 | submap = reset
420 | ```
421 |
422 | This works because the binds are executed in the order they appear, and
423 | assigning multiple actions per bind is possible.
424 |
425 | ### Nesting
426 |
427 | Submaps can be nested, see the following example:
428 |
429 | ```ini
430 | bind = $mainMod, M, submap, main_submap
431 | submap = main_submap
432 |
433 | # ...
434 |
435 | # nested_one
436 | bind = , 1, submap, nested_one
437 | submap = nested_one
438 |
439 | # ...
440 |
441 | bind = SHIFT, escape, submap, reset
442 | bind = , escape, submap, main_submap
443 | submap = main_submap
444 | # /nested_one
445 |
446 | # nested_two
447 | bind = , 2, submap, nested_two
448 | submap = nested_two
449 |
450 | # ...
451 |
452 | bind = SHIFT, escape, submap, reset
453 | bind = , escape, submap, main_submap
454 | submap = main_submap
455 | # /nested_two
456 |
457 | bind = , escape, submap, reset
458 | submap = reset
459 | ```
460 |
461 | ### Catch-All
462 |
463 | You can also define a keybind via the special `catchall` keyword, which
464 | activates no matter which key is pressed. This can be used to prevent any keys
465 | from passing to your active application while in a submap or to exit it
466 | immediately when any unknown key is pressed:
467 |
468 | ```ini
469 | bind = , catchall, submap, reset
470 | ```
471 |
472 | ## Example Binds
473 |
474 | ### Media
475 |
476 | These binds set the expected behavior for regular keyboard media volume keys,
477 | including when the screen is locked:
478 |
479 | ```ini
480 | bindel = , XF86AudioRaiseVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%+
481 | bindel = , XF86AudioLowerVolume, exec, wpctl set-volume @DEFAULT_AUDIO_SINK@ 5%-
482 | bindl = , XF86AudioMute, exec, wpctl set-mute @DEFAULT_AUDIO_SINK@ toggle
483 | # Requires playerctl
484 | bindl = , XF86AudioPlay, exec, playerctl play-pause
485 | bindl = , XF86AudioPrev, exec, playerctl previous
486 | bindl = , XF86AudioNext, exec, playerctl next
487 | ```
488 |
--------------------------------------------------------------------------------
/parser/data/sources/Configuring-Hyprland.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 1
3 | title: Configuring Hyprland
4 | ---
5 |
6 | The config is located in `~/.config/hypr/hyprland.conf`.
7 |
8 | You can tell Hyprland to use a specific configuration file by using the
9 | `--config` (or `-c`) argument.
10 |
11 | Hyprland will automatically generate an example config for you if you don't have
12 | one. You can find an example config
13 | [here](https://github.com/hyprwm/Hyprland/blob/main/example/hyprland.conf).
14 |
15 | By removing the line `autogenerated=1` you'll remove the yellow warning.
16 |
17 | The config is reloaded the moment you save it. However, you can use `hyprctl reload` to reload the config manually.
18 |
19 | Start a section with `name {` and end in `}` **_in separate lines!_**
20 |
21 | {{< callout >}}
22 |
23 | The default config is not complete and does not list all the options / features
24 | of Hyprland. Please refer to this wiki page and the pages linked further down
25 | here for full configuration instructions.
26 |
27 | **Make sure to read the [Variables](../Variables) page as well**. It covers all
28 | the toggleable / numerical options.
29 |
30 | {{< /callout >}}
31 |
32 | ## Line style
33 |
34 | Every config line is a command followed by a value.
35 |
36 | ```ini
37 | COMMAND=VALUE
38 | ```
39 |
40 | The command can be a variable, or a special keyword (described further in this
41 | page)
42 |
43 | You are **allowed to** input trailing spaces at the beginning and end.
44 |
45 | e.g.:
46 |
47 | ```ini
48 | COMMAND = VALUE
49 | ```
50 |
51 | is valid.
52 |
53 | ### Comments
54 |
55 | Comments are started with the `#` character.
56 |
57 | If you want to escape it (put an actual `#` and not start a comment) you can use
58 | `##`. It will be turned into a single `#` that WILL be a part of your line.
59 |
60 | ### Escaping errors
61 |
62 | If you use plugins, you may want to ignore errors from missing options/keywords
63 | so that you don't get an error bar before they are loaded. To do so, do this:
64 |
65 | ```ini
66 | # hyprlang noerror true
67 |
68 | bind = MOD, KEY, something, amogus
69 | someoption = blah
70 |
71 | # hyprlang noerror false
72 | ```
73 |
74 | ## Basic configuring
75 |
76 | To configure the "options" of Hyprland, animations, styling, etc. see
77 | [Variables](../Variables).
78 |
79 | ## Advanced configuring
80 |
81 | Some keywords (binds, curves, execs, monitors, etc.) are not variables but
82 | define special behavior.
83 |
84 | See all of them in [Keywords](../Keywords) and the sidebar.
85 |
--------------------------------------------------------------------------------
/parser/data/sources/Dwindle-Layout.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 11
3 | title: Dwindle Layout
4 | ---
5 |
6 | Dwindle is a BSPWM-like layout, where every window on a workspace is a member of
7 | a binary tree.
8 |
9 | ## Quirks
10 |
11 | Dwindle splits are NOT PERMANENT. The split is determined dynamically with the
12 | W/H ratio of the parent node. If W > H, it's side-by-side. If H > W, it's
13 | top-and-bottom. You can make them permanent by enabling `preserve_split`.
14 |
15 | ## Config
16 |
17 | category name: `dwindle`
18 |
19 | | name | description | type | default |
20 | | --- | --- | --- | --- |
21 | | pseudotile | enable pseudotiling. Pseudotiled windows retain their floating size when tiled. | bool | false |
22 | | force_split | 0 -> split follows mouse, 1 -> always split to the left (new = left or top) 2 -> always split to the right (new = right or bottom) | int | 0 |
23 | | preserve_split | if enabled, the split (side/top) will not change regardless of what happens to the container. | bool | false |
24 | | smart_split | if enabled, allows a more precise control over the window split direction based on the cursor's position. The window is conceptually divided into four triangles, and cursor's triangle determines the split direction. This feature also turns on preserve_split. | bool | false |
25 | | smart_resizing | if enabled, resizing direction will be determined by the mouse's position on the window (nearest to which corner). Else, it is based on the window's tiling position. | bool | true |
26 | | permanent_direction_override | if enabled, makes the preselect direction persist until either this mode is turned off, another direction is specified, or a non-direction is specified (anything other than l,r,u/t,d/b) | bool | false |
27 | | special_scale_factor | specifies the scale factor of windows on the special workspace [0 - 1] | float | 1 |
28 | | split_width_multiplier | specifies the auto-split width multiplier | float | 1.0 |
29 | | use_active_for_splits | whether to prefer the active window or the mouse position for splits | bool | true |
30 | | default_split_ratio | the default split ratio on window open. 1 means even 50/50 split. [0.1 - 1.9] | float | 1.0 |
31 | | split_bias | specifies which window will receive the larger half of a split. positional - 0, current window - 1, opening window - 2 [0/1/2] | int | 0 |
32 |
33 | ## Bind Dispatchers
34 |
35 | | dispatcher | description | params |
36 | | --- | --- | --- |
37 | | pseudo | toggles the given window's pseudo mode | left empty / `active` for current, or `window` for a specific window |
38 |
39 | ## Layout messages
40 |
41 | Dispatcher `layoutmsg` params:
42 |
43 | | param | description | args |
44 | | --- | --- | --- |
45 | | togglesplit | toggles the split (top/side) of the current window. `preserve_split` must be enabled for toggling to work. | none |
46 | | swapsplit | swaps the two halves of the split of the current window. | none |
47 | | preselect | A one-time override for the split direction. (valid for the next window to be opened, only works on tiled windows) | direction |
48 | | movetoroot | moves the selected window (active window if unspecified) to the root of its workspace tree. The default behavior maximizes the window in its current subtree. If `unstable` is provided as the second argument, the window will be swapped with the other subtree instead. It is not possible to only provide the second argument, but `movetoroot active unstable` will achieve the same result. | [window, [ string ]] |
49 |
50 | e.g.:
51 |
52 | ```ini
53 | bind = SUPER, A, layoutmsg, preselect l
54 | ```
55 |
--------------------------------------------------------------------------------
/parser/data/sources/Environment-variables.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 16
3 | title: Environment variables
4 | ---
5 |
6 | {{< callout type=info >}}
7 |
8 | [uwsm](../../Useful-Utilities/Systemd-start) users should avoid placing environment variables in the `hyprland.conf` file. Instead, use `~/.config/uwsm/env` for theming, xcursor, nvidia and toolkit variables, and `~/.config/uwsm/env-hyprland` for `HYPR*` and `AQ_*` variables. The format is `export KEY=VAL`.
9 |
10 | ```plain
11 | export XCURSOR_SIZE=24
12 | ```
13 |
14 | See [uwsm readme](https://github.com/Vladimir-csp/uwsm?tab=readme-ov-file#4-environments-and-shell-profile) for additional information.
15 |
16 | {{< /callout >}}
17 |
18 | You can use the `env` keyword to set environment variables prior to the
19 | initialization of the Display Server, e.g.:
20 |
21 | ```ini
22 | env = GTK_THEME,Nord
23 | ```
24 |
25 | {{< callout >}}
26 |
27 | Hyprland puts the raw string to the envvar with the `env` keyword. You should
28 | _not_ add quotes around the values.
29 |
30 | e.g.:
31 |
32 | ```ini
33 | env = QT_QPA_PLATFORM,wayland
34 | ```
35 |
36 | and _**NOT**_
37 |
38 | ```ini
39 | env = QT_QPA_PLATFORM,"wayland"
40 | ```
41 |
42 | {{< /callout >}}
43 |
44 | Please avoid putting those environment variables in /etc/environment. That will
45 | cause all sessions (including Xorg ones) to pick up your wayland-specific
46 | environment on traditional Linux distros.
47 |
48 | ## Hyprland Environment Variables
49 |
50 | - `HYPRLAND_TRACE=1` - Enables more verbose logging.
51 | - `HYPRLAND_NO_RT=1` - Disables realtime priority setting by Hyprland.
52 | - `HYPRLAND_NO_SD_NOTIFY=1` - If systemd, disables the `sd_notify` calls.
53 | - `HYPRLAND_NO_SD_VARS=1` - Disables management of variables in systemd and dbus activation environments.
54 | - `HYPRLAND_CONFIG` - Specifies where you want your Hyprland configuration.
55 |
56 | ## Aquamarine Environment Variables
57 |
58 | - `AQ_TRACE=1` - Enables more verbose logging.
59 | - `AQ_DRM_DEVICES=` - Set an explicit list of DRM devices (GPUs) to use. It's a colon-separated list of paths, with the first being the primary.
60 | E.g. `/dev/dri/card1:/dev/dri/card0`
61 | - `AQ_MGPU_NO_EXPLICIT=1` - Disables explicit syncing on mgpu buffers
62 | - `AQ_NO_MODIFIERS=1` - Disables modifiers for DRM buffers
63 |
64 | ## Toolkit Backend Variables
65 |
66 | - `env = GDK_BACKEND,wayland,x11,*` - GTK: Use wayland if available. If not: try x11, then any other GDK backend.
67 | - `env = QT_QPA_PLATFORM,wayland;xcb` - Qt: Use wayland if available, fall back to
68 | x11 if not.
69 | - `env = SDL_VIDEODRIVER,wayland` - Run SDL2 applications on Wayland. Remove or set to
70 | `x11` if games that provide older versions of SDL cause compatibility issues
71 | - `env = CLUTTER_BACKEND,wayland` - Clutter package already has wayland enabled, this
72 | variable will force Clutter applications to try and use the Wayland backend
73 |
74 | ## XDG Specifications
75 |
76 | - `env = XDG_CURRENT_DESKTOP,Hyprland`
77 | - `env = XDG_SESSION_TYPE,wayland`
78 | - `env = XDG_SESSION_DESKTOP,Hyprland`
79 |
80 | XDG specific environment variables are often detected through portals and
81 | applications that may set those for you, however it is not a bad idea to set
82 | them explicitly.
83 |
84 | If your [desktop portal](https://wiki.archlinux.org/title/XDG_Desktop_Portal) is malfunctioning for seemingly
85 | no reason (no errors), it's likely your XDG env isn't set correctly.
86 |
87 | {{< callout type=info >}}
88 |
89 | [uwsm](../../Useful-Utilities/Systemd-start) users don't need to explicitly set XDG environment variables, as uwsm sets them, automatically.
90 |
91 | {{< /callout >}}
92 |
93 | ## Qt Variables
94 |
95 | - `env = QT_AUTO_SCREEN_SCALE_FACTOR,1` -
96 | [(From the Qt documentation)](https://doc.qt.io/qt-5/highdpi.html) enables
97 | automatic scaling, based on the monitor's pixel density
98 | - `env = QT_QPA_PLATFORM,wayland;xcb` - Tell Qt applications to use the Wayland
99 | backend, and fall back to x11 if Wayland is unavailable
100 | - `env = QT_WAYLAND_DISABLE_WINDOWDECORATION,1` - Disables window decorations on Qt
101 | applications
102 | - `env = QT_QPA_PLATFORMTHEME,qt5ct` - Tells Qt based applications to pick your theme
103 | from qt5ct, use with Kvantum.
104 |
105 | ## NVIDIA Specific
106 |
107 | To force GBM as a backend, set the following environment variables:
108 |
109 | - `env = GBM_BACKEND,nvidia-drm`
110 | - `env = __GLX_VENDOR_LIBRARY_NAME,nvidia`
111 |
112 | > See
113 | > [Archwiki Wayland Page](https://wiki.archlinux.org/title/Wayland#Requirements)
114 | > for more details on those variables.
115 |
116 | - `env = LIBVA_DRIVER_NAME,nvidia` - Hardware acceleration on NVIDIA GPUs
117 |
118 | > See
119 | > [Archwiki Hardware Acceleration Page](https://wiki.archlinux.org/title/Hardware_video_acceleration)
120 | > for details and necessary values before setting this variable.
121 |
122 | - `__GL_GSYNC_ALLOWED` - Controls if G-Sync capable monitors should use Variable
123 | Refresh Rate (VRR)
124 |
125 | > See
126 | > [Nvidia Documentation](https://download.nvidia.com/XFree86/Linux-32bit-ARM/375.26/README/openglenvvariables.html)
127 | > for details.
128 |
129 | - `__GL_VRR_ALLOWED` - Controls if Adaptive Sync should be used. Recommended to
130 | set as "0" to avoid having problems on some games.
131 |
132 | - `env = AQ_NO_ATOMIC,1` - use legacy DRM interface instead of atomic mode
133 | setting. **NOT** recommended.
134 |
135 | ## Theming Related Variables
136 |
137 | - `GTK_THEME` - Set a GTK theme manually, for those who want to avoid appearance
138 | tools such as lxappearance or nwg-look
139 | - `XCURSOR_THEME` - Set your cursor theme. The theme needs to be installed and
140 | readable by your user.
141 | - `XCURSOR_SIZE` - Set cursor size. See [here](../../FAQ/) for why you might
142 | want this variable set.
143 |
--------------------------------------------------------------------------------
/parser/data/sources/Example-configurations.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 19
3 | title: Example configurations
4 | ---
5 |
6 | This page houses links to a few repositories with beautiful Hyprland
7 | configurations for you to get inspired from or learn how to configure Hyprland
8 | from a more tangible example.
9 |
10 | {{< callout >}}
11 |
12 | These configurations are popular and have many people using them. PRs that add
13 | more configurations will not be merged.
14 |
15 | {{< /callout >}}
16 |
17 | ### end_4
18 |
19 | 
20 |
21 | [https://github.com/end-4/dots-hyprland](https://github.com/end-4/dots-hyprland)
22 |
23 | ### Stephan Raabe (ML4W)
24 |
25 | 
26 |
27 | [https://github.com/mylinuxforwork/dotfiles](https://github.com/mylinuxforwork/dotfiles)
28 |
29 | ### fufexan
30 |
31 | 
32 |
33 | [https://github.com/fufexan/dotfiles](https://github.com/fufexan/dotfiles)
34 |
35 | ### linuxmobile
36 |
37 | 
38 |
39 | [https://github.com/linuxmobile/hyprland-dots](https://github.com/linuxmobile/hyprland-dots)
40 |
41 | ### flick0
42 |
43 | 
44 |
45 | [https://github.com/flick0/dotfiles](https://github.com/flick0/dotfiles)
46 |
47 | ### iamverysimp1e
48 |
49 | 
50 |
51 | [https://github.com/iamverysimp1e/dots](https://github.com/iamverysimp1e/dots)
52 |
53 | ### notusknot
54 |
55 | 
56 |
57 | [https://github.com/notusknot/dotfiles-nix](https://github.com/notusknot/dotfiles-nix)
58 |
59 | ### coffebar
60 |
61 | 
62 |
63 | [https://github.com/coffebar/dotfiles](https://github.com/coffebar/dotfiles)
64 |
--------------------------------------------------------------------------------
/parser/data/sources/Expanding-functionality.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 14
3 | title: Expanding functionality
4 | ---
5 |
6 | Hyprland exposes two powerful sockets for you to use.
7 |
8 | The first, socket1, can be fully controlled with `hyprctl`, see its usage
9 | [here](../Using-hyprctl).
10 |
11 | The second, socket2, sends events for certain changes / actions and can be used
12 | to react to different events. See its description [here](../../IPC/).
13 |
14 | ## Example script
15 |
16 | This bash script will change the outer gaps to 20 if the currently focused
17 | monitor is DP-1, and 30 otherwise.
18 |
19 | ```bash
20 | #!/usr/bin/env bash
21 |
22 | function handle {
23 | if [[ ${1:0:10} == "focusedmon" ]]; then
24 | if [[ ${1:12:4} == "DP-1" ]]; then
25 | hyprctl keyword general:gaps_out 20
26 | else
27 | hyprctl keyword general:gaps_out 30
28 | fi
29 | fi
30 | }
31 |
32 | socat - "UNIX-CONNECT:$XDG_RUNTIME_DIR/hypr/$HYPRLAND_INSTANCE_SIGNATURE/.socket2.sock" | while read -r line; do handle "$line"; done
33 | ```
34 |
--------------------------------------------------------------------------------
/parser/data/sources/Keywords.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 3
3 | title: Keywords
4 | ---
5 |
6 | Keywords are not variables, but "commands" for more advanced configuring. On
7 | this page, you will be presented with some that do not deserve their own page.
8 |
9 | See the sidebar for more keywords to control binds, animations, monitors, et
10 | cetera.
11 |
12 | {{< callout >}}
13 |
14 | Please remember, that for ALL arguments separated by a comma, if you want to
15 | leave one of them empty, you cannot reduce the number of commas, _unless told
16 | otherwise in a specific section_:
17 |
18 | ```ini
19 | three_param_keyword = A, B, C # OK
20 | three_param_keyword = A, C # NOT OK
21 | three_param_keyword = A, , C # OK
22 | three_param_keyword = A, B, # OK
23 | ```
24 |
25 | {{< /callout >}}
26 |
27 | ## Executing
28 |
29 | You can execute a shell script on:
30 |
31 | - startup of the compositor
32 | - every time the config is reloaded.
33 | - shutdown of the compositor
34 |
35 | `exec-once = command` will execute only on launch ([support rules](../Dispatchers/#executing-with-rules))
36 |
37 | `execr-once = command` will execute only on launch
38 |
39 | `exec = command` will execute on each reload ([support rules](../Dispatchers/#executing-with-rules))
40 |
41 | `execr = command` will execute on each reload
42 |
43 | `exec-shutdown = command` will execute only on shutdown
44 |
45 | ## Sourcing (multi-file)
46 |
47 | Use the `source` keyword to source another file.
48 |
49 | For example, in your `hyprland.conf` you can:
50 |
51 | ```ini
52 | source = ~/.config/hypr/myColors.conf
53 | ```
54 |
55 | And Hyprland will enter that file and parse it like a Hyprland config.
56 |
57 | Please note it's LINEAR. Meaning lines above the `source =` will be parsed first,
58 | then lines inside `~/.config/hypr/myColors.conf`, then lines below.
59 |
60 | ## Gestures
61 |
62 | Use [libinput-gestures](https://github.com/bulletmark/libinput-gestures) with
63 | `hyprctl` if you want to expand Hyprland's gestures beyond what's offered in
64 | [Variables](../Variables).
65 |
66 | ## Per-device input configs
67 |
68 | Per-device config options will overwrite your options set in the `input`
69 | section. It's worth noting that ONLY values explicitly changed will be
70 | overwritten.
71 |
72 | In order to apply per-device config options, add a new category like this:
73 |
74 | ```ini
75 | device {
76 | name = ...
77 | # options ...
78 | }
79 | ```
80 |
81 | The `name` can be easily obtained by checking the output of `hyprctl devices`.
82 |
83 | Inside of it, put your config options. All options from the `input` category
84 | (and all subcategories, e.g. `input:touchpad`) can be put inside, **EXCEPT**:
85 |
86 | - `force_no_accel`
87 | - `follow_mouse`
88 | - `float_switch_override_focus`
89 | - `scroll_factor`
90 |
91 | Properties that change names:
92 |
93 | ```plain
94 | touchdevice:transform -> transform
95 | touchdevice:output -> output
96 | ```
97 |
98 | You can also use the `output` setting for tablets to bind them to outputs.
99 | Remember to use the name of the `Tablet` and not `Tablet Pad` or `Tablet tool`.
100 |
101 | Additional properties only present in per-device configs:
102 |
103 | - `enabled` -> (only for mice / touchpads / touchdevices / keyboards)
104 | - enables / disables the device (connects / disconnects from the on-screen cursor)
105 | - default: Enabled
106 | - `keybinds` -> (only for devices that send key events)
107 | - enables / disables keybinds for the device
108 | - default: Enabled
109 |
110 | Example config section:
111 |
112 | ```ini
113 | device {
114 | name = royuan-akko-multi-modes-keyboard-b
115 | repeat_rate = 50
116 | repeat_delay = 500
117 | middle_button_emulation = 0
118 | }
119 | ```
120 |
121 | Example modifying per-device config values using `hyprctl`:
122 |
123 | ```bash
124 | hyprctl -r -- keyword device[my-device]:sensitivity -1
125 | ```
126 |
127 | {{< callout type=info >}}
128 |
129 | Per-device layouts will by default not alter the keybind keymap, so for example
130 | with a global keymap of `us` and a per-device one of `fr`, the keybinds will
131 | still act as if you were on `us`.
132 |
133 | You can change this behavior by setting `resolve_binds_by_sym = 1`. In that case
134 | you'll need to type the symbol specified in the bind to activate it.
135 |
136 | {{< /callout >}}
137 |
138 | ## Wallpapers
139 |
140 | The "Hyprland" background you see when you first start Hyprland is **NOT A
141 | WALLPAPER**, it's the default image rendered at the bottom of the render stack.
142 |
143 | To set a wallpaper, use a wallpaper utility like
144 | [hyprpaper](https://github.com/hyprwm/hyprpaper) or
145 | [swaybg](https://github.com/swaywm/swaybg).
146 |
147 | More can be found in [Useful Utilities](../../Useful-Utilities).
148 |
149 | ## Blurring layerSurfaces
150 |
151 | Layer surfaces are not windows. These are, for example: wallpapers,
152 | notification overlays, bars, etc.
153 |
154 | If you want to blur them, use a layer rule:
155 |
156 | ```ini
157 | layerrule = blur, NAMESPACE
158 | # or
159 | layerrule = blur, address:0x
160 | ```
161 |
162 | You can get the namespace / address from `hyprctl layers`.
163 |
164 | To remove a layer rule (useful in dynamic situations) use:
165 |
166 | ```ini
167 | layerrule = unset,
168 | ```
169 |
170 | For example:
171 |
172 | ```ini
173 | layerrule = unset, NAMESPACE
174 | ```
175 |
176 | ## Setting the environment
177 |
178 | {{< callout type=info >}}
179 |
180 | The `env` keyword works just like `exec-once`, meaning it will only fire once on
181 | Hyprland's launch.
182 |
183 | {{< /callout >}}
184 |
185 | You can use the `env` keyword to set environment variables when Hyprland starts,
186 | e.g:
187 |
188 | ```ini
189 | env = XCURSOR_SIZE,24
190 | ```
191 |
192 | You can also add a `d` flag if you want the env var to be exported to D-Bus
193 | (systemd only):
194 |
195 | ```ini
196 | envd = XCURSOR_SIZE,24
197 | ```
198 |
199 | {{< callout >}}
200 |
201 | Hyprland puts the raw string to the env var. You should _not_ add quotes around
202 | the values.
203 |
204 | e.g.:
205 |
206 | ```ini
207 | env = QT_QPA_PLATFORM,wayland
208 | ```
209 |
210 | and _**NOT**_
211 |
212 | ```ini
213 | env = QT_QPA_PLATFORM,"wayland"
214 | ```
215 |
216 | {{< /callout >}}
217 |
--------------------------------------------------------------------------------
/parser/data/sources/Master-Layout.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 11
3 | title: Master Layout
4 | ---
5 |
6 | The master layout makes one (or more) window(s) be the "master", taking (by
7 | default) the left part of the screen, and tiles the rest on the right. You can
8 | change the orientation on a per-workspace basis if you want to use anything other
9 | than the default left/right split.
10 |
11 | 
12 |
13 | ## Config
14 |
15 | _category name `master`_
16 |
17 | | name | description | type | default |
18 | | --- | --- | --- | --- |
19 | | allow_small_split | enable adding additional master windows in a horizontal split style | bool | false |
20 | | special_scale_factor | the scale of the special workspace windows. [0.0 - 1.0] | float | 1 |
21 | | mfact | the size as a percentage of the master window, for example `mfact = 0.70` would mean 70% of the screen will be the master window, and 30% the slave [0.0 - 1.0] | floatvalue | 0.55 |
22 | | new_status | `master`: new window becomes master; `slave`: new windows are added to slave stack; `inherit`: inherit from focused window | string | `slave` |
23 | | new_on_top | whether a newly open window should be on the top of the stack | bool | false |
24 | | new_on_active | `before`, `after`: place new window relative to the focused window; `none`: place new window according to the value of `new_on_top`. | string | `none` |
25 | | orientation | default placement of the master area, can be left, right, top, bottom or center | string | left |
26 | | inherit_fullscreen | inherit fullscreen status when cycling/swapping to another window (e.g. monocle layout) | bool | true |
27 | | slave_count_for_center_master | when using orientation=center, make the master window centered only when at least this many slave windows are open. (Set 0 to always_center_master) | int | 2 |
28 | | center_master_fallback | Set fallback for center master when slaves are less than slave_count_for_center_master, can be left ,right ,top ,bottom | string | left |
29 | | smart_resizing | if enabled, resizing direction will be determined by the mouse's position on the window (nearest to which corner). Else, it is based on the window's tiling position. | bool | true |
30 | | drop_at_cursor | when enabled, dragging and dropping windows will put them at the cursor position. Otherwise, when dropped at the stack side, they will go to the top/bottom of the stack depending on new_on_top. | bool | true |
31 | | always_keep_position | whether to keep the master window in its configured position when there are no slave windows | bool | false |
32 |
33 | ## Dispatchers
34 |
35 | `layoutmsg` commands:
36 |
37 | | command | description | params |
38 | | --- | --- | --- |
39 | | swapwithmaster | swaps the current window with master. If the current window is the master, swaps it with the first child. | either `master` (new focus is the new master window), `child` (new focus is the new child) or `auto` (which is the default, keeps the focus of the previously focused window) |
40 | | focusmaster | focuses the master window. | either `master` (focus stays at master, even if it was selected before) or `auto` (which is the default, if the current window is the master, focuses the first child.) |
41 | | cyclenext | focuses the next window respecting the layout | either `loop` (allow looping from the bottom of the pile back to master) or `noloop` (force stop at the bottom of the pile, like in DWM). `loop` is the default if left blank. |
42 | | cycleprev | focuses the previous window respecting the layout | either `loop` (allow looping from master to the bottom of the pile) or `noloop` (force stop at master, like in DWM). `loop` is the default if left blank. |
43 | | swapnext | swaps the focused window with the next window respecting the layout | either `loop` (allow swapping the bottom of the pile and master) or `noloop` (do not allow it, like in DWM). `loop` is the default if left blank. |
44 | | swapprev | swaps the focused window with the previous window respecting the layout | either `loop` (allow swapping master and the bottom of the pile) or `noloop` (do not allow it, like in DWM). `loop` is the default if left blank. |
45 | | addmaster | adds a master to the master side. That will be the active window, if it's not a master, or the first non-master window. | none |
46 | | removemaster | removes a master from the master side. That will be the active window, if it's a master, or the last master window. | none |
47 | | orientationleft | sets the orientation for the current workspace to left (master area left, slave windows to the right, vertically stacked) | none |
48 | | orientationright | sets the orientation for the current workspace to right (master area right, slave windows to the left, vertically stacked) | none |
49 | | orientationtop | sets the orientation for the current workspace to top (master area top, slave windows to the bottom, horizontally stacked) | none |
50 | | orientationbottom | sets the orientation for the current workspace to bottom (master area bottom, slave windows to the top, horizontally stacked) | none |
51 | | orientationcenter | sets the orientation for the current workspace to center (master area center, slave windows alternate to the left and right, vertically stacked) | none |
52 | | orientationnext | cycle to the next orientation for the current workspace (clockwise) | none |
53 | | orientationprev | cycle to the previous orientation for the current workspace (counter-clockwise) | none |
54 | | orientationcycle | cycle to the next orientation from the provided list, for the current workspace | allowed values: `left`, `top`, `right`, `bottom`, or `center`. The values have to be separated by a space. If left empty, it will work like `orientationnext` |
55 | | mfact | change mfact, the master split ratio | the new split ratio, a relative float delta (e.g `-0.2` or `+0.2`) or `exact` followed by a the exact float value between 0.0 and 1.0 |
56 | | rollnext | rotate the next window in stack to be the master, while keeping the focus on master | none |
57 | | rollprev | rotate the previous window in stack to be the master, while keeping the focus on master | none |
58 |
59 | Parameters for the commands are separated by a single space.
60 |
61 | {{< callout type=info >}}
62 |
63 | Example usage:
64 |
65 | ```ini
66 | bind = MOD, KEY, layoutmsg, cyclenext
67 | # behaves like xmonads promote feature (https://hackage.haskell.org/package/xmonad-contrib-0.17.1/docs/XMonad-Actions-Promote.html)
68 | bind = MOD, KEY, layoutmsg, swapwithmaster master
69 | ```
70 |
71 | {{< /callout >}}
72 |
73 | ## Workspace Rules
74 |
75 | `layoutopt` rules:
76 |
77 | | rule | description | type |
78 | | --- | --- | --- |
79 | | orientation:[o] | Sets the orientation of a workspace. For available orientations, see [Config->orientation](#config) | string |
80 |
81 | Example usage:
82 |
83 | ```ini
84 | workspace = 2, layoutopt:orientation:top
85 | ```
86 |
--------------------------------------------------------------------------------
/parser/data/sources/Monitors.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 4
3 | title: Monitors
4 | ---
5 |
6 | ## General
7 |
8 | The general config of a monitor looks like this:
9 |
10 | ```ini
11 | monitor = name, resolution, position, scale
12 | ```
13 |
14 | A common example:
15 |
16 | ```ini
17 | monitor = DP-1, 1920x1080@144, 0x0, 1
18 | ```
19 |
20 | This will make the monitor on `DP-1` a `1920x1080` display, at
21 | 144Hz, `0x0` off from the top left corner, with a scale of 1 (unscaled).
22 |
23 | To list all available monitors (active and inactive):
24 |
25 | ```bash
26 | hyprctl monitors all
27 | ```
28 |
29 | Monitors are positioned on a virtual "layout". The `position` is the position,
30 | in pixels, of said display in the layout. (calculated from the top-left corner)
31 |
32 | For example:
33 |
34 | ```ini
35 | monitor = DP-1, 1920x1080, 0x0, 1
36 | monitor = DP-2, 1920x1080, 1920x0, 1
37 | ```
38 |
39 | will tell Hyprland to put DP-1 on the _left_ of DP-2, while
40 |
41 | ```ini
42 | monitor = DP-1, 1920x1080, 1920x0, 1
43 | monitor = DP-2, 1920x1080, 0x0, 1
44 | ```
45 |
46 | will tell Hyprland to put DP-1 on the _right_.
47 |
48 | The `position` may contain _negative_ values, so the above example could also be
49 | written as
50 |
51 | ```ini
52 | monitor = DP-1, 1920x1080, 0x0, 1
53 | monitor = DP-2, 1920x1080, -1920x0, 1
54 | ```
55 |
56 | Hyprland uses an inverse Y cartesian system. Thus, a negative y coordinate
57 | places a monitor higher, and a positive y coordinate will place it lower.
58 |
59 | For example:
60 |
61 | ```ini
62 | monitor = DP-1, 1920x1080, 0x0, 1
63 | monitor = DP-2, 1920x1080, 0x-1080, 1
64 | ```
65 |
66 | will tell Hyprland to put DP-2 _above_ DP-1, while
67 |
68 | ```ini
69 | monitor = DP-1, 1920x1080, 0x0, 1
70 | monitor = DP-2, 1920x1080, 0x1080, 1
71 | ```
72 |
73 | will tell Hyprland to put DP-2 _below_.
74 |
75 | {{< callout type=info >}}
76 |
77 | The position is calculated with the scaled (and transformed) resolution, meaning
78 | if you want your 4K monitor with scale 2 to the left of your 1080p one, you'd
79 | use the position `1920x0` for the second screen (3840 / 2). If the monitor is
80 | also rotated 90 degrees (vertical), you'd use `1080x0`.
81 |
82 | {{ callout >}}
83 |
84 | Leaving the name empty will define a fallback rule to use when no other rules
85 | match.
86 |
87 | There are a few special values for the resolutions:
88 |
89 | - `preferred` - use the display's preferred size and refresh rate.
90 | - `highres` - use the highest supported resolution.
91 | - `highrr` - use the highest supported refresh rate.
92 |
93 | Position also has a few special values:
94 |
95 | - `auto` - let Hyprland decide on a position. By default, it places each new monitor to the right of existing ones.
96 | - `auto-right/left/up/down` - place the monitor to the right/left, above or below other monitors.
97 |
98 | _**Please Note:**_ While specifying a monitor direction for your first monitor is allowed, this does nothing and it will
99 | be positioned at (0,0). Also the direction is always from the center out, so you can specify `auto-up` then `auto-left`,
100 | but the left monitors will just be left of the origin and above the origin. You can also specify duplicate directions and
101 | monitors will continue to go in that direction.
102 |
103 | You can also use `auto` as a scale to let Hyprland decide on a scale for you.
104 | These depend on the PPI of the monitor.
105 |
106 | Recommended rule for quickly plugging in random monitors:
107 |
108 | ```ini
109 | monitor = , preferred, auto, 1
110 | ```
111 |
112 | This will make any monitor that was not specified with an explicit rule
113 | automatically placed on the right of the other(s), with its preferred
114 | resolution.
115 |
116 | For more specific rules, you can also use the output's description (see
117 | `hyprctl monitors` for more details). If the output of `hyprctl monitors` looks
118 | like the following:
119 |
120 | ```yaml
121 | Monitor eDP-1 (ID 0):
122 | 1920x1080@60.00100 at 0x0
123 | description: Chimei Innolux Corporation 0x150C (eDP-1)
124 | make: Chimei Innolux Corporation
125 | model: 0x150C
126 | [...]
127 | ```
128 |
129 | then the `description` value up to, but not including the portname `(eDP-1)` can
130 | be used to specify the monitor:
131 |
132 | ```ini
133 | monitor = desc:Chimei Innolux Corporation 0x150C, preferred, auto, 1.5
134 | ```
135 |
136 | Remember to remove the `(portname)`!
137 |
138 | ### Custom modelines
139 |
140 | You can set up a custom modeline by changing the resolution field to a modeline,
141 | for example:
142 |
143 | ```ini
144 | monitor = DP-1, modeline 1071.101 3840 3848 3880 3920 2160 2263 2271 2277 +hsync -vsync, 0x0, 1
145 | ```
146 |
147 | ### Disabling a monitor
148 |
149 | To disable a monitor, use
150 |
151 | ```ini
152 | monitor = name, disable
153 | ```
154 |
155 | {{< callout >}}
156 |
157 | Disabling a monitor will literally remove it from the layout, moving all windows
158 | and workspaces to any remaining ones. If you want to disable your monitor in a
159 | screensaver style (just turn off the monitor) use the `dpms`
160 | [dispatcher](../Dispatchers).
161 |
162 | {{ callout >}}
163 |
164 | ## Custom reserved area
165 |
166 | A reserved area is an area that remains unoccupied by tiled windows.
167 | If your workflow requires a custom reserved area, you can add it with:
168 |
169 | ```ini
170 | monitor = name, addreserved, TOP, BOTTOM, LEFT, RIGHT
171 | ```
172 |
173 | Where `TOP` `BOTTOM` `LEFT` `RIGHT` are integers, i.e the number in pixels of
174 | the reserved area to add. This does stack on top of the calculated reserved area
175 | (e.g. bars), but you may only use one of these rules per monitor in the config.
176 |
177 | ## Extra args
178 |
179 | You can combine extra arguments at the end of the monitor rule, examples:
180 |
181 | ```ini
182 | monitor = eDP-1, 2880x1800@90, 0x0, 1, transform, 1, mirror, DP-2, bitdepth, 10
183 | ```
184 |
185 | See below for more details about each argument.
186 |
187 | ### Mirrored displays
188 |
189 | If you want to mirror a display, add a `, mirror, ` at the end of the
190 | monitor rule, examples:
191 |
192 | ```ini
193 | monitor = DP-3, 1920x1080@60, 0x0, 1, mirror, DP-2
194 | monitor = , preferred, auto, 1, mirror, DP-1
195 | ```
196 |
197 | Please remember that mirroring displays will not "re-render" everything for your
198 | second monitor, so if mirroring a 1080p screen onto a 4K one, the resolution
199 | will still be 1080p on the 4K display. This also means squishing and stretching
200 | will occur on aspect ratios that differ (e.g 16:9 and 16:10).
201 |
202 | ### 10 bit support
203 |
204 | If you want to enable 10 bit support for your display, add a `, bitdepth, 10` at
205 | the end of the monitor rule, e.g:
206 |
207 | ```ini
208 | monitor = eDP-1, 2880x1800@90, 0x0, 1, bitdepth, 10
209 | ```
210 |
211 | {{< callout >}}
212 |
213 | Colors registered in Hyprland (e.g. the border color) do _not_ support
214 | 10 bit.
215 |
216 | Some applications do _not_ support screen capture with 10 bit enabled.
217 |
218 | {{< /callout >}}
219 |
220 | ### Color management presets
221 |
222 | Add a `, cm, X` to change default sRGB output preset
223 |
224 | ```ini
225 | monitor = eDP-1, 2880x1800@90, 0x0, 1, bitdepth, 10, cm, wide
226 | ```
227 |
228 | ```plain
229 | auto - srgb for 8bpc, wide for 10bpc if supported (recommended)
230 | srgb - sRGB primaries (default)
231 | wide - wide color gamut, BT2020 primaries
232 | edid - primaries from edid (known to be inaccurate)
233 | hdr - wide color gamut and HDR PQ transfer function (experimental)
234 | hdredid - same as hdr with edid primaries (experimental)
235 | ```
236 |
237 | Fullscreen HDR is possible without hdr `cm` setting if `render:cm_fs_passthrough` is enabled.
238 |
239 | Use `sdrbrightness, B` and `sdrsaturation, S` to control SDR brighness and saturation in HDR mode. The default for both values is `1.0`. Typical brightness value should be in `1.0 ... 2.0` range.
240 |
241 | ```ini
242 | monitor = eDP-1, 2880x1800@90, 0x0, 1, bitdepth, 10, cm, hdr, sdrbrightness, 1.2, sdrsaturation, 0.98
243 | ```
244 |
245 | ### VRR
246 |
247 | Per-display VRR can be done by adding `, vrr, X` where `X` is the mode from the
248 | [variables page](../Variables).
249 |
250 | ## Rotating
251 |
252 | If you want to rotate a monitor, add a `, transform, X` at the end of the monitor
253 | rule, where `X` corresponds to a transform number, e.g.:
254 |
255 | ```ini
256 | monitor = eDP-1, 2880x1800@90, 0x0, 1, transform, 1
257 | ```
258 |
259 | Transform list:
260 |
261 | ```plain
262 | 0 -> normal (no transforms)
263 | 1 -> 90 degrees
264 | 2 -> 180 degrees
265 | 3 -> 270 degrees
266 | 4 -> flipped
267 | 5 -> flipped + 90 degrees
268 | 6 -> flipped + 180 degrees
269 | 7 -> flipped + 270 degrees
270 | ```
271 |
272 | ## Default workspace
273 |
274 | See [Workspace Rules](../Workspace-Rules).
275 |
276 | ### Binding workspaces to a monitor
277 |
278 | See [Workspace Rules](../Workspace-Rules).
279 |
--------------------------------------------------------------------------------
/parser/data/sources/Multi-GPU.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 17
3 | title: Multi-GPU
4 | ---
5 |
6 | ## General
7 |
8 | If your host machine uses multiple GPUs, you may want to use one GPU
9 | for rendering all the elements for Hyprland including windows, animations, and
10 | another for hardware acceleration for certain applications, etc.
11 |
12 | This setup is very common in the likes of gaming laptops, GPU-passthrough
13 | (without VFIO) capable hosts, and if you have multiple GPUs in general.
14 |
15 | ## Detecting GPUs
16 |
17 | For this case, the writer is taking the example of their laptop.
18 |
19 | Upon running `lspci -d ::03xx`, one can list all the PCI display controllers
20 | available.
21 |
22 | ```plain
23 | 01:00.0 VGA compatible controller: NVIDIA Corporation TU117M [GeForce GTX 1650 Mobile / Max-Q] (rev a1)
24 | 06:00.0 VGA compatible controller: Advanced Micro Devices, Inc. [AMD/ATI] Cezanne [Radeon Vega Series / Radeon Vega Mobile Series] (rev c6)
25 | ```
26 |
27 | Here it is clear that 2 GPUs are available, the dedicated NVIDIA GTX 1650 Mobile
28 | / Max-Q and the integrated AMD Cezanne Radeon Vega Series GPU.
29 |
30 | Now, run `ls -l /dev/dri/by-path`
31 |
32 | ```plain
33 | total 0
34 | lrwxrwxrwx 1 root root 8 Jul 14 15:45 pci-0000:01:00.0-card -> ../card0
35 | lrwxrwxrwx 1 root root 13 Jul 14 15:45 pci-0000:01:00.0-render -> ../renderD128
36 | lrwxrwxrwx 1 root root 8 Jul 14 15:45 pci-0000:06:00.0-card -> ../card1
37 | lrwxrwxrwx 1 root root 13 Jul 14 15:45 pci-0000:06:00.0-render -> ../renderD129
38 | ```
39 |
40 | So from the above outputs, we can see that the path for the AMD card is
41 | `pci-0000:06:00.0-card`, due to the matching `06:00.0` from the first command.
42 | Do not use the `card1` symlink indicated here. It is dynamically assigned at
43 | boot and is subject to frequent change, making it unsuitable as a marker for GPU selection.
44 |
45 | ## Telling Hyprland which GPU to use
46 |
47 | After determining which "card" belongs to which GPU, we can now tell
48 | Hyprland which GPUs to use by setting the `AQ_DRM_DEVICES` environment variable.
49 |
50 | {{< callout type=info >}}
51 |
52 | It is generally a good idea for laptops to use the integrated GPU as the primary
53 | renderer as this preserves battery life and is practically indistinguishable
54 | from using the dedicated GPU on modern systems in most cases. Hyprland can be
55 | run on integrated GPUs just fine. The same principle applies for desktop setups
56 | with lower and higher power rating GPUs respectively.
57 |
58 | {{< /callout >}}
59 |
60 | If you would like to use another GPU, or the wrong GPU is picked by default,
61 | set `AQ_DRM_DEVICES` to a `:`-separated list of card paths, e.g.
62 |
63 | ```plain
64 | env = AQ_DRM_DEVICES,/dev/dri/card0:/dev/dri/card1
65 | ```
66 |
67 | Here, we tell Hyprland which GPUs it's allowed to use, in order of priority.
68 | For example, `card0` will be the primary renderer, but if it isn't available for
69 | whatever reason, then `card1` is primary.
70 |
71 | Do note that if you have an external monitor connected to, for example `card1`,
72 | that card must be included in `AQ_DRM_DEVICES` for the monitor to work, though
73 | it doesn't have to be the primary renderer.
74 |
75 | You should now be able to use an integrated GPU for lighter GPU loads,
76 | including Hyprland, or default to your dGPU if you prefer.
77 |
78 | {{< callout type=info >}}
79 |
80 | [uwsm](../../Useful-Utilities/Systemd-start) users are advised to export the `AQ_DRM_DEVICES` variable inside `~/.config/uwsm/env-hyprland`, instead.
81 | This method ensures that the variable is properly exported to the systemd environment without conflicting with other compositors or desktop environments.
82 |
83 | ```plain
84 | export AQ_DRM_DEVICES="/dev/dri/card0:/dev/dri/card1"
85 | ```
86 |
87 | {{< /callout >}}
88 |
--------------------------------------------------------------------------------
/parser/data/sources/Performance.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 20
3 | title: Performance
4 | ---
5 |
6 | This page documents known tricks and fixes to boost performance if by any chance
7 | you stumble upon problems or you do not care that much about animations.
8 |
9 | ## Fractional scaling
10 |
11 | Wayland fractional scaling is a lot better than before, but it is not perfect.
12 | Some applications do not support it yet or the support is experimental at best.
13 | If you have problems with your graphics card having high usage or Hyprland
14 | feeling laggy, try setting the scaling to integer numbers such as `1` or `2`
15 | like in this example `monitor=,preferred,auto,2`.
16 |
17 | ## Low FPS/stutter/FPS drops on Intel iGPU with TLP (mainly laptops)
18 |
19 | The TLP defaults are rather aggressive, setting `INTEL_GPU_MIN_FREQ_ON_AC`
20 | and/or `INTEL_GPU_MIN_FREQ_ON_BAT` in `/etc/tlp.conf` to something slightly
21 | higher (e.g. to 500 from 300) will reduce stutter significantly or, in the best
22 | case, remove it completely.
23 |
24 | ## How do I make Hyprland draw as little power as possible on my laptop?
25 |
26 | **_Useful Optimizations_**:
27 |
28 | - `decoration:blur:enabled = false` and `decoration:shadow:enabled = false` to disable
29 | fancy but battery hungry effects.
30 |
31 | - `misc:vfr = true`, since it'll lower the amount of sent frames when nothing is
32 | happening on-screen.
33 |
34 | ## My games work poorly, especially proton ones
35 |
36 | Using `gamescope` tends to fix any and all issues with Wayland/Hyprland.
37 |
--------------------------------------------------------------------------------
/parser/data/sources/Permissions.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 12
3 | title: Permissions
4 | ---
5 |
6 | If you have `hyprland-qtutils` installed, you can make use of Hyprland's built-in
7 | permission system.
8 |
9 | For now, it only has a few permissions, but it might include more in the future.
10 |
11 | ## Permissions
12 |
13 | Permissions work a bit like Android ones. If an app tries to do something sensitive with
14 | the compositor (Hyprland), Hyprland will pop up a notification asking you if you
15 | want to let it do that.
16 |
17 | {{< callout type=info >}}
18 |
19 | Before setting up permissions, make sure you enable them by setting
20 | `ecosystem:enforce_permissions = true`, as it's disabled by default.
21 |
22 | {{ callout >}}
23 |
24 |
25 | ### Configuring permissions
26 |
27 | {{< callout type=important >}}
28 |
29 | Permissions set up in the config are **not** reloaded on-the-fly and require a Hyprland
30 | restart for security reasons.
31 |
32 | {{ callout >}}
33 |
34 | Configuring them is simple:
35 |
36 | ```ini
37 | permission = regex, permission, mode
38 | ```
39 |
40 | for example:
41 | ```ini
42 | permission = /usr/bin/grim, screencopy, allow
43 | ```
44 | Will allow `/usr/bin/grim` to always capture your screen without asking.
45 |
46 | ```ini
47 | permission = /usr/bin/appsuite-.*, screencopy, allow
48 | ```
49 | Will allow any app whose path starts with `/usr/bin/appsuite-` to capture your screen without asking.
50 |
51 |
52 | ### Permisision modes
53 |
54 | There are 3 modes:
55 | - `allow`: Don't ask, just allow the app to proceed.
56 | - `ask`: Pop up a notification every time the app tries to do something sensitive. These popups allow you to Deny, Allow until the app exits, or Allow until Hyprland exits.
57 | - `deny`: Don't ask, always deny the application access.
58 |
59 |
60 | ### Permission list
61 |
62 | `screencopy`:
63 | - Default: **ASK**
64 | - Access to your screen _without_ going through xdg-desktop-portal-hyprland. Examples include: `grim`, `wl-screenrec`, `wf-recorder`.
65 | - If denied, will render a black screen with a "permission denied" text.
66 | - Why deny? For apps / scripts that might maliciously try to capture your screen without your knowledge by using wayland protocols directly.
67 |
68 | `plugin`:
69 | - Default: **ASK**
70 | - Access to load a plugin. Can be either a regex for the app binary, or plugin path.
71 | - Do _not_ allow `hyprctl` to load your plugins by default (attacker could issue `hyprctl plugin load /tmp/my-malicious-plugin.so`) - use either `deny` to disable or `ask` to be prompted.
72 |
73 | `keyboard`:
74 | - Default: **ALLOW**
75 | - Access to connecting a new keyboard. Regex of the device name.
76 | - If you want to disable all keyboards not matching a regex, make a rule that sets `DENY` for `.*` _as the last keyboard permission rule_.
77 | - Why deny? Rubber duckies, malicious virtual / usb keyboards.
78 |
79 | ## Notes
80 |
81 | **xdg-desktop-portal** implementations (including xdph) are just regular applications. They will go through permissions too. You might want to consider
82 | adding a rule like this:
83 | ```ini
84 | permission = /usr/(lib|libexec|lib64)/xdg-desktop-portal-hyprland, screencopy, allow
85 | ```
86 | if you are not allowing screencopy for all apps.
87 |
88 |
89 |
90 | On some **BSD** systems paths might not work. In such cases, you might want to disable permissions altogether, by setting
91 | ```ini
92 | ecosystem {
93 | enforce_permissions = false
94 | }
95 | ```
96 | otherwise, you have no _config_ control over permissions (popups will still work, although will not show paths, and "remember" will not be available).
97 |
98 |
--------------------------------------------------------------------------------
/parser/data/sources/Start.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 1
3 | title: Start
4 | ---
5 |
6 | The config is located in `$XDG_CONFIG_HOME/hypr/hyprland.conf`. In most cases,
7 | that maps to `~/.config/hypr/hyprland.conf`.
8 |
9 | You can tell Hyprland to use a specific configuration file by using the
10 | `--config` (or `-c`) argument.
11 |
12 | Hyprland will automatically generate an example config for you if you don't have
13 | one. You can find an example config
14 | [here](https://github.com/hyprwm/Hyprland/blob/main/example/hyprland.conf).
15 |
16 | By removing the line containing `autogenerated=1` you'll remove the yellow
17 | warning.
18 |
19 | The config is reloaded the moment you save it. However, you can use
20 | `hyprctl reload` to reload the config manually.
21 |
22 | Start a section with `name {` and end in `}` **_in separate lines!_**
23 |
24 | {{< callout >}}
25 |
26 | The default config is not complete and does not list all the options / features
27 | of Hyprland. Please refer to this wiki page and the pages linked further down
28 | here for full configuration instructions.
29 |
30 | **Make sure to read the [Variables](../Variables) page as well**. It covers all
31 | the toggleable / numerical options.
32 |
33 | {{< /callout >}}
34 |
35 | ## Language style and syntax
36 |
37 | See the [hyprlang page](../../Hypr-Ecosystem/hyprlang).
38 |
39 | ## Basic configuring
40 |
41 | To configure Hyprland's options, animations, styling, etc. see
42 | [Variables](../Variables).
43 |
44 | ## Advanced configuring
45 |
46 | Some keywords (binds, curves, execs, monitors, etc.) are not variables but
47 | define special behavior.
48 |
49 | See all of them in [Keywords](../Keywords) and the sidebar.
--------------------------------------------------------------------------------
/parser/data/sources/Tearing.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 10
3 | title: Tearing
4 | ---
5 |
6 | Screen tearing is used to reduce latency and/or jitter in games.
7 |
8 | ## Enabling tearing
9 |
10 | To enable tearing:
11 |
12 | - Set `general:allow_tearing` to `true`. This is a "master toggle"
13 | - Add an `immediate` windowrule to your game of choice. This makes sure that
14 | Hyprland will tear it.
15 |
16 | {{< callout >}}
17 |
18 | Please note that tearing will only be in effect when the game is in fullscreen
19 | and the only thing visible on the screen.
20 |
21 | {{< /callout >}}
22 |
23 | Example snippet:
24 |
25 | ```env
26 | general {
27 | allow_tearing = true
28 | }
29 |
30 | windowrule = immediate, class:^(cs2)$
31 | ```
32 |
33 | {{< callout type=warning >}}
34 |
35 | If you experience graphical issues, you may be out of luck. Tearing support is
36 | experimental.
37 |
38 | See the likely culprits below.
39 |
40 | {{< /callout >}}
41 |
42 | ## Common issues
43 |
44 | ### No tearing at all
45 |
46 | Make sure your window rules are matching and you have the master toggle enabled.
47 |
48 | Also make sure nothing except for your game is showing on your monitor. No
49 | notifications, overlays, lockscreens, bars, other windows, etc. (on a different
50 | monitor is fine)
51 |
52 | ### Apps that should tear, freeze
53 |
54 | Almost definitely means your GPU driver does not support tearing.
55 |
56 | Please _do not_ report issues if this is the culprit.
57 |
58 | ### Graphical artifacts (random colorful pixels, etc)
59 |
60 | Likely issue with your graphics driver.
61 |
62 | Please _do not_ report issues if this is the culprit. Unfortunately, it's most
63 | likely your GPU driver's fault.
64 |
--------------------------------------------------------------------------------
/parser/data/sources/Uncommon-tips-&-tricks.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 18
3 | title: Uncommon tips & tricks
4 | ---
5 |
6 | ## Switchable keyboard layouts
7 |
8 | The easiest way to accomplish this is to set this using XKB settings, for
9 | example:
10 |
11 | ```ini
12 | input {
13 | kb_layout = us,cz
14 | kb_variant = ,qwerty
15 | kb_options = grp:alt_shift_toggle
16 | }
17 | ```
18 |
19 | Variants are set per layout.
20 |
21 | {{< callout >}}
22 |
23 | The first layout defined in the input section will be the one used for binds by
24 | default.
25 |
26 | For example: `us,ua` -> config binds would be e.g. `SUPER, A`, while on `ua,us`
27 | -> `SUPER, Cyrillic_ef`
28 |
29 | You can change this behavior globally or per-device by setting
30 | `resolve_binds_by_sym = 1`. In that case, binds will activate when the symbol
31 | typed matches the symbol specified in the bind.
32 |
33 | For example: if your layouts are `us,fr` and have a bind for `SUPER, A` you'd
34 | need to press the first letter on the second row while the `us` layout is active
35 | and the first letter on the first row while the `fr` layout is active.
36 |
37 | {{< /callout >}}
38 |
39 | You can also bind a key to execute `hyprctl switchxkblayout` for more keybind
40 | freedom. See [Using hyprctl](../Using-hyprctl).
41 |
42 | To find the valid layouts and `kb_options`, you can check out the
43 | `/usr/share/X11/xkb/rules/base.lst`. For example:
44 |
45 | To get the layout name of a language:
46 |
47 | ```sh
48 | grep -i 'persian' /usr/share/X11/xkb/rules/base.lst
49 | ```
50 |
51 | To get the list of keyboard shortcuts you can put in the `kb_options` to toggle
52 | keyboard layouts:
53 |
54 | ```sh
55 | grep 'grp:.*toggle' /usr/share/X11/xkb/rules/base.lst
56 | ```
57 |
58 | ## Disabling keybinds with one master keybind
59 |
60 | If you want to disable all keybinds with another keybind (make a keybind toggle
61 | of sorts) you can just use a submap with only a keybind to exit it.
62 |
63 | ```ini
64 | bind = MOD, KEY, submap, clean
65 | submap = clean
66 | bind = MOD, KEY, submap, reset
67 | submap = reset
68 | ```
69 |
70 | ## Remap Caps-Lock to Ctrl
71 |
72 | ```ini
73 | input {
74 | kb_options = ctrl:nocaps
75 | }
76 | ```
77 |
78 | ## Swap Caps-Lock and Escape
79 |
80 | ```ini
81 | input {
82 | kb_options = caps:swapescape
83 | }
84 | ```
85 |
86 | ## Set F13-F24 as usual function keys
87 |
88 | By default, F13-F24 are mapped by xkb as various "XF86" keysyms. These cause binding
89 | issues in many programs. One example is OBS Studio, which does not detect the XF86
90 | keysyms as usable keybindings, making you unable to use them for binds. This option
91 | simply maps them back to the expected F13-F24 values, which are bindable as normal.
92 |
93 | {{< callout >}}
94 | This option was only added recently to `xkeyboard-config`. Please ensure you are on version
95 | 2.43 or greater for this option to do anything.
96 | {{< /callout >}}
97 |
98 | ```ini
99 | input {
100 | kb_options = fkeys:basic_13-24
101 | }
102 | ```
103 |
104 | ## Minimize windows using special workspaces
105 |
106 | This approach uses special workspaces to mimic the "minimize window" function, by using a single keybind to toggle the minimized state.
107 | Note that one keybind can only handle one window.
108 |
109 | ```ini
110 | bind = $mod, S, togglespecialworkspace, magic
111 | bind = $mod, S, movetoworkspace, +0
112 | bind = $mod, S, togglespecialworkspace, magic
113 | bind = $mod, S, movetoworkspace, special:magic
114 | bind = $mod, S, togglespecialworkspace, magic
115 | ```
116 |
117 | ## Show desktop
118 |
119 | This approach uses same principle as the [Minimize windows using special workspaces](#minimize-windows-using-special-workspaces) section.
120 | It moves all windows from current workspace to a special workspace named `desktop`.
121 | Showing desktop state is remembered per workspace.
122 |
123 | Create a script:
124 |
125 | ```sh
126 | #!/bin/env sh
127 |
128 | TMP_FILE="$XDG_RUNTIME_DIR/hyprland-show-desktop"
129 |
130 | CURRENT_WORKSPACE=$(hyprctl monitors -j | jq '.[] | .activeWorkspace | .name' | sed 's/"//g')
131 |
132 | if [ -s "$TMP_FILE-$CURRENT_WORKSPACE" ]; then
133 | readarray -d $'\n' -t ADDRESS_ARRAY <<< $(< "$TMP_FILE-$CURRENT_WORKSPACE")
134 |
135 | for address in "${ADDRESS_ARRAY[@]}"
136 | do
137 | CMDS+="dispatch movetoworkspacesilent name:$CURRENT_WORKSPACE,address:$address;"
138 | done
139 |
140 | hyprctl --batch "$CMDS"
141 |
142 | rm "$TMP_FILE-$CURRENT_WORKSPACE"
143 | else
144 | HIDDEN_WINDOWS=$(hyprctl clients -j | jq --arg CW "$CURRENT_WORKSPACE" '.[] | select (.workspace .name == $CW) | .address')
145 |
146 | readarray -d $'\n' -t ADDRESS_ARRAY <<< $HIDDEN_WINDOWS
147 |
148 | for address in "${ADDRESS_ARRAY[@]}"
149 | do
150 | address=$(sed 's/"//g' <<< $address )
151 |
152 | if [[ -n address ]]; then
153 | TMP_ADDRESS+="$address\n"
154 | fi
155 |
156 | CMDS+="dispatch movetoworkspacesilent special:desktop,address:$address;"
157 | done
158 |
159 | hyprctl --batch "$CMDS"
160 |
161 | echo -e "$TMP_ADDRESS" | sed -e '/^$/d' > "$TMP_FILE-$CURRENT_WORKSPACE"
162 | fi
163 | ```
164 |
165 | then bind it:
166 |
167 | ```ini
168 | bind = $mainMod , D, exec,
169 | ```
170 |
171 | ## Minimize Steam instead of killing
172 |
173 | Steam will exit entirely when its last window is closed using the `killactive`
174 | dispatcher. To minimize Steam to tray, use the following script to close
175 | applications:
176 |
177 | ```sh
178 | if [ "$(hyprctl activewindow -j | jq -r ".class")" = "Steam" ]; then
179 | xdotool getactivewindow windowunmap
180 | else
181 | hyprctl dispatch killactive ""
182 | fi
183 | ```
184 |
185 | ## Shimeji
186 |
187 | To use Shimeji programs like
188 | [this](https://codeberg.org/thatonecalculator/spamton-linux-shimeji), set the
189 | following rules:
190 |
191 | ```ini
192 | windowrule = float, class:com-group_finity-mascot-Main
193 | windowrule = noblur, class:com-group_finity-mascot-Main
194 | windowrule = nofocus, class:com-group_finity-mascot-Main
195 | windowrule = noshadow, class:com-group_finity-mascot-Main
196 | windowrule = noborder, class:com-group_finity-mascot-Main
197 | ```
198 |
199 | {{< callout type=info >}}
200 |
201 | The app indicator probably won't show, so you'll have to `killall -9 java` to
202 | kill them.
203 |
204 | {{< /callout >}}
205 |
206 | 
207 |
208 | ## Toggle animations/blur/etc hotkey
209 |
210 | For increased performance in games, or for less distractions at a keypress
211 |
212 | 1. create file
213 | `~/.config/hypr/gamemode.sh && chmod +x ~/.config/hypr/gamemode.sh` and add:
214 |
215 | ```bash
216 | #!/usr/bin/env sh
217 | HYPRGAMEMODE=$(hyprctl getoption animations:enabled | awk 'NR==1{print $2}')
218 | if [ "$HYPRGAMEMODE" = 1 ] ; then
219 | hyprctl --batch "\
220 | keyword animations:enabled 0;\
221 | keyword decoration:shadow:enabled 0;\
222 | keyword decoration:blur:enabled 0;\
223 | keyword general:gaps_in 0;\
224 | keyword general:gaps_out 0;\
225 | keyword general:border_size 1;\
226 | keyword decoration:rounding 0"
227 | exit
228 | fi
229 | hyprctl reload
230 | ```
231 |
232 | Edit to your liking of course. If animations are enabled, it disables all the
233 | pretty stuff. Otherwise, the script reloads your config to grab your defaults.
234 |
235 | 2. Add this to your `hyprland.conf`:
236 |
237 | ```ini
238 | bind = WIN, F1, exec, ~/.config/hypr/gamemode.sh
239 | ```
240 |
241 | The hotkey toggle will be WIN+F1, but you can change this to whatever you want.
242 |
243 | ## Zoom
244 |
245 | To zoom using Hyprland's built-in zoom utility
246 | {{< callout >}}
247 | If mouse wheel bindings work only for the first time, you should probably reduce reset time with `binds:scroll_event_delay`
248 | {{< /callout >}}
249 |
250 | ```ini
251 | bind = $mod, mouse_down, exec, hyprctl -q keyword cursor:zoom_factor $(hyprctl getoption cursor:zoom_factor | awk '/^float.*/ {print $2 * 1.1}')
252 | bind = $mod, mouse_up, exec, hyprctl -q keyword cursor:zoom_factor $(hyprctl getoption cursor:zoom_factor | awk '/^float.*/ {print $2 * 0.9}')
253 |
254 | binde = $mod, equal, exec, hyprctl -q keyword cursor:zoom_factor $(hyprctl getoption cursor:zoom_factor | awk '/^float.*/ {print $2 * 1.1}')
255 | binde = $mod, minus, exec, hyprctl -q keyword cursor:zoom_factor $(hyprctl getoption cursor:zoom_factor | awk '/^float.*/ {print $2 * 0.9}')
256 | binde = $mod, KP_ADD, exec, hyprctl -q keyword cursor:zoom_factor $(hyprctl getoption cursor:zoom_factor | awk '/^float.*/ {print $2 * 1.1}')
257 | binde = $mod, KP_SUBTRACT, exec, hyprctl -q keyword cursor:zoom_factor $(hyprctl getoption cursor:zoom_factor | awk '/^float.*/ {print $2 * 0.9}')
258 |
259 | bind = $mod SHIFT, mouse_up, exec, hyprctl -q keyword cursor:zoom_factor 1
260 | bind = $mod SHIFT, mouse_down, exec, hyprctl -q keyword cursor:zoom_factor 1
261 | bind = $mod SHIFT, minus, exec, hyprctl -q keyword cursor:zoom_factor 1
262 | bind = $mod SHIFT, KP_SUBTRACT, exec, hyprctl -q keyword cursor:zoom_factor 1
263 | bind = $mod SHIFT, 0, exec, hyprctl -q keyword cursor:zoom_factor 1
264 | ```
265 |
266 | ## Alt tab behaviour
267 | To mimic DE's alt-tab behaviour. Here is an example that uses foot, fzf, [grim-hyprland](https://github.com/eriedaberrie/grim-hyprland) and chafa to the screenshot in the terminal.
268 |
269 | 
270 |
271 | Dependencies :
272 | - foot
273 | - fzf
274 | - [grim-hyprland](https://github.com/eriedaberrie/grim-hyprland)
275 | - chafa
276 | - jq
277 |
278 | 1. add this to your config
279 |
280 | ```ini
281 | exec-once = foot --server
282 |
283 | bind = ALT, tab, exec, hyprctl -q keyword animations:enabled false ; hyprctl -q dispatch exec "footclient -a alttab $XDG_CONFIG_HOME/hypr/scripts/alttab/alttab.sh" ; hyprctl -q keyword unbind "ALT, TAB" ; hyprctl -q dispatch submap alttab
284 |
285 | submap=alttab
286 | bind = ALT, tab, sendshortcut, , tab, class:alttab
287 | bind = ALT SHIFT, tab, sendshortcut, shift, tab, class:alttab
288 |
289 | bindrt = ALT, ALT_L, exec, $XDG_CONFIG_HOME/hypr/scripts/alttab/disable.sh ; hyprctl -q dispatch sendshortcut ,return,class:alttab
290 | bind = ALT, escape, exec, $XDG_CONFIG_HOME/hypr/scripts/alttab/disable.sh ; hyprctl -q dispatch sendshortcut ,escape,class:alttab
291 | submap = reset
292 |
293 | workspace = special:alttab, gapsout:0, gapsin:0, bordersize:0
294 | windowrule = noanim, class:alttab
295 | windowrule = stayfocused, class:alttab
296 | windowrule = workspace special:alttab, class:alttab
297 | windowrule = bordersize 0, class:alttab
298 | ```
299 |
300 | 2. create file `touch $XDG_CONFIG_HOME/hypr/scripts/alttab/alttab.sh && chmod +x $XDG_CONFIG_HOME/hypr/scripts/alttab/alttab.sh` and add:
301 |
302 | ```bash {filename="alttab.sh"}
303 | #!/usr/bin/env bash
304 | address=$(hyprctl -j clients | jq -r 'sort_by(.focusHistoryID) | .[] | select(.workspace.id >= 0) | "\(.address)\t\(.title)"' |
305 | fzf --color prompt:green,pointer:green,current-bg:-1,current-fg:green,gutter:-1,border:bright-black,current-hl:red,hl:red \
306 | --cycle \
307 | --sync \
308 | --bind tab:down,shift-tab:up,start:down,double-click:ignore \
309 | --wrap \
310 | --delimiter=$'\t' \
311 | --with-nth=2 \
312 | --preview "$XDG_CONFIG_HOME/hypr/scripts/alttab/preview.sh {}" \
313 | --preview-window=down:80% \
314 | --layout=reverse |
315 | awk -F"\t" '{print $1}')
316 |
317 | if [ -n "$address" ] ; then
318 | hyprctl --batch -q "dispatch focuswindow address:$address;dispatch alterzorder top"
319 | fi
320 |
321 | hyprctl -q dispatch submap reset
322 | ```
323 |
324 | I chose to exclude windows that are in special workspaces but it can be modified by removing `select(.workspace.id >= 0)`
325 |
326 | 3. create file `touch $XDG_CONFIG_HOME/hypr/scripts/alttab/preview.sh && chmod +x $XDG_CONFIG_HOME/hypr/scripts/alttab/preview.sh` and add:
327 |
328 | ```bash {filename="preview.sh"}
329 | #!/usr/bin/env bash
330 | line="$1"
331 |
332 | IFS=$'\t' read -r addr _ <<< "$line"
333 | dim=${FZF_PREVIEW_COLUMNS}x${FZF_PREVIEW_LINES}
334 |
335 | grim -t png -l 0 -w "$addr" ~.config/hypr/scripts/alttab/preview.png
336 | chafa --animate false -s "$dim" "$XDG_CONFIG_HOME/hypr/scripts/alttab/preview.png"
337 | ```
338 |
339 | 4. create file `touch $XDG_CONFIG_HOME/hypr/scripts/alttab/disable.sh && chmod +x $XDG_CONFIG_HOME/hypr/scripts/alttab/disable.sh` and add:
340 |
341 | ```bash {filename="disable.sh"}
342 | #!/usr/bin/env bash
343 | hyprctl -q keyword animations:enabled true
344 |
345 | hyprctl -q keyword unbind "ALT, tab"
346 | hyprctl -q keyword bind ALT, tab, exec, "hyprctl -q keyword animations:enabled false ; hyprctl -q dispatch exec 'footclient -a alttab $XDG_CONFIG_HOME/hypr/scripts/alttab/alttab.sh' ; hyprctl -q keyword unbind 'ALT, tab' ; hyprctl -q dispatch submap alttab"
347 | ```
348 |
--------------------------------------------------------------------------------
/parser/data/sources/Using-hyprctl.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 13
3 | title: Using hyprctl
4 | ---
5 |
6 | `hyprctl` is a utility for controlling some parts of the compositor from a CLI
7 | or a script. It should automatically be installed along with Hyprland.
8 |
9 | {{< callout type=warning >}}
10 |
11 | _hyprctl_ calls will be dispatched by the compositor _synchronously_, meaning
12 | any spam of the utility will cause slowdowns. It's recommended to use `--batch`
13 | for many control calls, and limiting the amount of info calls.
14 |
15 | For live event handling, see the [socket2](../../IPC/).
16 |
17 | {{< /callout >}}
18 |
19 | ## Commands
20 |
21 | ### dispatch
22 |
23 | Issue a `dispatch` to call a keybind dispatcher with an argument.
24 |
25 | An argument has to be present, for dispatchers without parameters it can be
26 | anything.
27 |
28 | To pass an argument starting with `-` or `--`, such as command line options
29 | to `exec` programs, pass `--` as an option. This will disable any subsequent
30 | parsing of options by _hyprctl_.
31 |
32 | Examples:
33 |
34 | ```sh
35 | hyprctl dispatch exec kitty
36 |
37 | hyprctl dispatch -- exec kitty --single-instance
38 |
39 | hyprctl dispatch pseudo x
40 | ```
41 |
42 | Returns: `ok` on success, an error message on fail.
43 |
44 | See [Dispatchers](../Dispatchers) for a list of dispatchers.
45 |
46 | ### keyword
47 |
48 | issue a `keyword` to call a config keyword dynamically.
49 |
50 | Examples:
51 |
52 | ```sh
53 | hyprctl keyword bind SUPER,O,pseudo
54 |
55 | hyprctl keyword general:border_size 10
56 |
57 | hyprctl keyword monitor DP-3,1920x1080@144,0x0,1
58 | ```
59 |
60 | Returns: `ok` on success, an error message on fail.
61 |
62 | ### reload
63 |
64 | Issue a `reload` to force reload the config.
65 |
66 | ### kill
67 |
68 | Issue a `kill` to get into a kill mode, where you can kill an app by clicking on
69 | it. You can exit it with ESCAPE.
70 |
71 | Kind of like xkill.
72 |
73 | ### setcursor
74 |
75 | Sets the cursor theme and reloads the cursor manager. Will set the theme for
76 | everything except GTK, because GTK.
77 |
78 | Please note that since 0.37.0, this only accepts hyprcursor themes. For legacy xcursor themes,
79 | use the `XCURSOR_THEME` and `XCURSOR_SIZE` env vars.
80 |
81 | params: theme and size
82 |
83 | e.g.:
84 |
85 | ```sh
86 | hyprctl setcursor Bibata-Modern-Classic 24
87 | ```
88 |
89 | ### output
90 |
91 | Allows you to add and remove fake outputs to your preferred backend.
92 |
93 | Usage:
94 |
95 | ```sh
96 | hyprctl output create [backend] (name)
97 | ```
98 |
99 | or
100 |
101 | ```sh
102 | hyprctl output remove [name]
103 | ```
104 |
105 | Where `[backend]` is the name of the backend and `(name)` is an optional name
106 | for the output. If `(name)` is not specified, the default naming scheme will be
107 | used (`HEADLESS-2`, `WL-1`, etc.)
108 |
109 | {{< callout type=info >}}
110 |
111 | `create` and `remove` can also be `add` or `destroy`, respectively.
112 |
113 | {{< /callout >}}
114 |
115 | Available backends:
116 |
117 | - `wayland`: Creates an output as a Wayland window. This will only work if
118 | you're already running Hyprland with the Wayland backend.
119 | - `headless`: Creates a headless monitor output. If you're running a VNC/RDP/
120 | Sunshine server, you should use this.
121 | - `auto`: Picks a backend for you. For example, if you're running Hyprland from
122 | the TTY, `headless` will be chosen.
123 |
124 | For example, to create a headless output named "test":
125 |
126 | ```sh
127 | hyprctl output create headless test
128 | ```
129 |
130 | And to remove it:
131 |
132 | ```sh
133 | hyprctl output remove test
134 | ```
135 |
136 | ### switchxkblayout
137 |
138 | Sets the xkb layout index for a keyboard.
139 |
140 | For example, if you set:
141 |
142 | ```ini
143 | device {
144 | name = my-epic-keyboard-v1
145 | kb_layout = us,pl,de
146 | }
147 | ```
148 |
149 | You can use this command to switch between them.
150 |
151 | ```sh
152 | hyprctl switchxkblayout [DEVICE] [CMD]
153 | ```
154 |
155 | where `CMD` is either `next` for next, `prev` for previous, or `ID` for a
156 | specific one (in the above case, `us`: 0, `pl`: 1, `de`: 2). You can find the
157 | `DEVICE` using `hyprctl devices` command.
158 |
159 | `DEVICE` can also be `current` or `all`, self-explanatory. Current is the `main` keyboard from `devices`.
160 |
161 | Example command for a typical keyboard:
162 |
163 | ```sh
164 | hyprctl switchxkblayout at-translated-set-2-keyboard next
165 | ```
166 |
167 | {{< callout type=info >}}
168 |
169 | If you want a single variant i.e. pl/dvorak on one layout but us/qwerty on the
170 | other, xkb parameters can still be blank, however the amount of comma-separated
171 | parameters have to match. Alternatively, a single parameter can be specified for
172 | it to apply to all three.
173 |
174 | ```ini
175 | input {
176 | kb_layout = pl,us,ru
177 | kb_variant = dvorak,,
178 | kb_options = caps:ctrl_modifier
179 | }
180 | ```
181 |
182 | {{< /callout >}}
183 |
184 | ### seterror
185 |
186 | Sets the hyprctl error string. Will reset when Hyprland's config is reloaded.
187 |
188 | ```sh
189 | hyprctl seterror 'rgba(66ee66ff)' hello world this is my problem
190 | ```
191 |
192 | To disable:
193 |
194 | ```sh
195 | hyprctl seterror disable
196 | ```
197 |
198 | ### notify
199 |
200 | Sends a notification using the built-in Hyprland notification system.
201 |
202 | ```sh
203 | hyprctl notify [ICON] [TIME_MS] [COLOR] [MESSAGE]
204 | ```
205 |
206 | For example:
207 |
208 | ```sh
209 | hyprctl notify -1 10000 "rgb(ff1ea3)" "Hello everyone!"
210 | ```
211 |
212 | Icon of `-1` means "No icon"
213 |
214 | Color of `0` means "Default color for icon"
215 |
216 | Icon list:
217 |
218 | ```sh
219 | WARNING = 0
220 | INFO = 1
221 | HINT = 2
222 | ERROR = 3
223 | CONFUSED = 4
224 | OK = 5
225 | ```
226 |
227 | Optionally, you can specify a font size of the notification like so:
228 |
229 | ```sh
230 | hyprctl notify -1 10000 "rgb(ff0000)" "fontsize:35 This text is big"
231 | ```
232 |
233 | The default font-size is 13.
234 |
235 | ### dismissnotify
236 |
237 | Dismisses all or up to AMOUNT notifications.
238 |
239 | ```sh
240 | hyprctl dismissnotify # dismiss all notifications
241 | hyprctl dismissnotify 2 # dismiss the oldest 2 notifications
242 | hyprctl dismissnotify -1 # dismiss all notifications (same as no arguments)
243 | ```
244 |
245 | ## Info
246 |
247 | ```plain
248 | version - prints the Hyprland version along with flags, commit and branch of build.
249 | monitors - lists active outputs with their properties, 'monitors all' lists active and inactive outputs
250 | workspaces - lists all workspaces with their properties
251 | activeworkspace - gets the active workspace and its properties
252 | workspacerules - gets the list of defined workspace rules
253 | clients - lists all windows with their properties
254 | devices - lists all connected keyboards and mice
255 | decorations [window] - lists all decorations and their info
256 | binds - lists all registered binds
257 | activewindow - gets the active window name and its properties
258 | layers - lists all the layers
259 | splash - prints the current random splash
260 | getoption [option] - gets the config option status (values)
261 | cursorpos - gets the current cursor position in global layout coordinates
262 | animations - gets the currently configured info about animations and beziers
263 | instances - lists all running instances of Hyprland with their info
264 | layouts - lists all layouts available (including from plugins)
265 | configerrors - lists all current config parsing errors
266 | rollinglog - prints tail of the log. Also supports -f/--follow option
267 | locked - prints whether the current session is locked.
268 | descriptions - returns a JSON with all config options, their descriptions and types.
269 | submap - prints the current submap the keybinds are in
270 | ```
271 |
272 | For the getoption command, the option name should be written as
273 | `section:option`, e.g.:
274 |
275 | ```sh
276 | hyprctl getoption general:border_size
277 |
278 | # For nested sections:
279 | hyprctl getoption input:touchpad:disable_while_typing
280 | ```
281 |
282 | See [Variables](../Variables) for sections and options you can use.
283 |
284 | ## Batch
285 |
286 | You can also use `--batch` to specify a batch of commands to execute.
287 |
288 | e.g.
289 |
290 | ```sh
291 | hyprctl --batch "keyword general:border_size 2 ; keyword general:gaps_out 20"
292 | ```
293 |
294 | `;` separates the commands
295 |
296 | ## Flags
297 |
298 | You can specify flags for the request like this:
299 |
300 | ```sh
301 | hyprctl -j monitors
302 | ```
303 |
304 | flag list:
305 |
306 | ```txt
307 | j -> output in JSON
308 | i -> select instance (id or index in hyprctl instances)
309 | ```
310 |
--------------------------------------------------------------------------------
/parser/data/sources/Workspace-Rules.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 8
3 | title: Workspace Rules
4 | ---
5 |
6 | You can set workspace rules to achieve workspace-specific behaviors. For
7 | instance, you can define a workspace where all windows are drawn without borders
8 | or gaps.
9 |
10 | For layout-specific rules, see the specific layout page. For example:
11 | [Master Layout->Workspace Rules](../Master-Layout#workspace-rules).
12 |
13 | ### Workspace selectors
14 |
15 | Workspaces that have already been created can be targeted by workspace
16 | selectors, e.g. `r[2-4] w[t1]`.
17 |
18 | Selectors have props separated by a space. No spaces are allowed inside props
19 | themselves.
20 |
21 | Props:
22 |
23 | - `r[A-B]` - ID range from A to B inclusive
24 | - `s[bool]` - Whether the workspace is special or not
25 | - `n[bool]`, `n[s:string]`, `n[e:string]` - named actions. `n[bool]` ->
26 | whether a workspace is a named workspace, `s` and `e` are starts and ends
27 | with respectively
28 | - `m[monitor]` - Monitor selector
29 | - `w[(flags)A-B]`, `w[(flags)X]` - Prop for window counts on the workspace.
30 | A-B is an inclusive range, X is a specific number. Flags can be omitted.
31 | It can be `t` for tiled-only, `f` for floating-only, `g` to count groups
32 | instead of windows, `v` to count only visible windows, and `p` to count
33 | only pinned windows.
34 | - `f[-1]`, `f[0]`, `f[1]`, `f[2]` - fullscreen state of the workspace. `-1`: no
35 | fullscreen, `0`: fullscreen, `1`: maximized, `2`, fullscreen without
36 | fullscreen state sent to the window.
37 |
38 | ### Syntax
39 |
40 | ```ini
41 | workspace = WORKSPACE, RULES
42 | ```
43 |
44 | - WORKSPACE is a valid workspace identifier (see
45 | [Dispatchers->Workspaces](../Dispatchers#workspaces)). This field is
46 | mandatory. This _can be_ a workspace selector, but please note
47 | workspace selectors can only match _existing_ workspaces.
48 | - RULES is one (or more) rule(s) as described here in [rules](#rules).
49 |
50 | ### Examples
51 |
52 | ```ini
53 | workspace = name:myworkspace, gapsin:0, gapsout:0
54 | workspace = 3, rounding:false, bordersize:0
55 | workspace = w[tg1-4], shadow:false
56 | ```
57 |
58 | #### Smart gaps
59 |
60 | To replicate "smart gaps" / "no gaps when only" from other WMs/Compositors, use this bad boy:
61 |
62 | ```ini
63 | workspace = w[tv1], gapsout:0, gapsin:0
64 | workspace = f[1], gapsout:0, gapsin:0
65 | windowrule = bordersize 0, floating:0, onworkspace:w[tv1]
66 | windowrule = rounding 0, floating:0, onworkspace:w[tv1]
67 | windowrule = bordersize 0, floating:0, onworkspace:f[1]
68 | windowrule = rounding 0, floating:0, onworkspace:f[1]
69 | ```
70 |
71 | #### Smart gaps (ignoring special workspaces)
72 |
73 | You can combine workspace selectors for more fine-grained control, for example, to ignore special workspaces:
74 |
75 | ```ini
76 | workspace = w[tv1]s[false], gapsout:0, gapsin:0
77 | workspace = f[1]s[false], gapsout:0, gapsin:0
78 | windowrule = bordersize 0, floating:0, onworkspace:w[tv1]s[false]
79 | windowrule = rounding 0, floating:0, onworkspace:w[tv1]s[false]
80 | windowrule = bordersize 0, floating:0, onworkspace:f[1]s[false]
81 | windowrule = rounding 0, floating:0, onworkspace:f[1]s[false]
82 | ```
83 |
84 | ## Rules
85 |
86 | | Rule | Description | type |
87 | | --- | --- | --- |
88 | | monitor:[m] | Binds a workspace to a monitor. See [syntax](#syntax) and [Monitors](../Monitors). | string |
89 | | default:[b] | Whether this workspace should be the default workspace for the given monitor | bool |
90 | | gapsin:[x] | Set the gaps between windows (equivalent to [General->gaps_in](../Variables#general)) | int |
91 | | gapsout:[x] | Set the gaps between windows and monitor edges (equivalent to [General->gaps_out](../Variables#general)) | int |
92 | | bordersize:[x] | Set the border size around windows (equivalent to [General->border_size](../Variables#general)) | int |
93 | | border:[b] | Whether to draw borders or not | bool |
94 | | shadow:[b] | Whether to draw shadows or not | bool |
95 | | rounding:[b] | Whether to draw rounded windows or not | bool |
96 | | decorate:[b] | Whether to draw window decorations or not | bool |
97 | | persistent:[b] | Keep this workspace alive even if empty and inactive | bool |
98 | | on-created-empty:[c] | A command to be executed once a workspace is created empty (i.e. not created by moving a window to it). See the [command syntax](../Dispatchers#executing-with-rules) | string |
99 | | defaultName:[s] | A default name for the workspace. | string |
100 |
101 | ### Example Rules
102 |
103 | ```ini
104 | workspace = 3, rounding:false, decorate:false
105 | workspace = name:coding, rounding:false, decorate:false, gapsin:0, gapsout:0, border:false, monitor:DP-1
106 | workspace = 8,bordersize:8
107 | workspace = name:Hello, monitor:DP-1, default:true
108 | workspace = name:gaming, monitor:desc:Chimei Innolux Corporation 0x150C, default:true
109 | workspace = 5, on-created-empty:[float] firefox
110 | workspace = special:scratchpad, on-created-empty:foot
111 | ```
112 |
--------------------------------------------------------------------------------
/parser/data/sources/XWayland.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 15
3 | title: XWayland
4 | ---
5 |
6 | XWayland is the bridging mechanism between legacy Xorg programs and Wayland
7 | compositors.
8 |
9 | ## HiDPI XWayland
10 |
11 | XWayland currently looks pixelated on HiDPI screens, due to Xorg's inability to
12 | scale.
13 |
14 | This problem is mitigated by the
15 | [`xwayland:force_zero_scaling`](../Variables/#xwayland) option,
16 | which forces XWayland windows not to be scaled.
17 |
18 | This will get rid of the pixelated look, but will not scale applications
19 | properly. To do this, each toolkit has its own mechanism.
20 |
21 | ```ini
22 | # change monitor to high resolution, the last argument is the scale factor
23 | monitor = , highres, auto, 2
24 |
25 | # unscale XWayland
26 | xwayland {
27 | force_zero_scaling = true
28 | }
29 |
30 | # toolkit-specific scale
31 | env = GDK_SCALE,2
32 | env = XCURSOR_SIZE,32
33 | ```
34 |
35 | The GDK_SCALE variable won't conflict with Wayland-native GTK programs.
36 |
37 | {{< callout >}}
38 |
39 | XWayland HiDPI patches are no longer supported. Do not use them.
40 |
41 | {{ callout >}}
42 |
43 | ## Abstract Unix domain socket
44 |
45 | X11 applications use Unix domain sockets to communicate with XWayland. On Linux, libX11 prefers
46 | to use the abstract Unix domain socket. This type of socket uses a separate, abstract namespace that
47 | is independent of the host filesystem. This makes abstract sockets more flexible
48 | but harder to [isolate](https://github.com/hyprwm/Hyprland/pull/8874)
49 | for some kinds of sandboxes like Flatpak. However, removing the abstract socket
50 | has [potential](https://gitlab.gnome.org/GNOME/mutter/-/issues/1613) security
51 | and compatibility issues.
52 |
53 | Keeping that in mind, we add the [`xwayland:create_abstract_socket`](../Variables/#xwayland) option.
54 | When the abstract socket is disabled, only the regular Unix domain
55 | socket will be created.
56 |
57 | _\* Abstract Unix domain sockets are available only on Linux-based systems_
58 |
--------------------------------------------------------------------------------
/parser/data/sources/_index.md:
--------------------------------------------------------------------------------
1 | ---
2 | weight: 3
3 | title: Configuring
4 | sidebar:
5 | open: true
6 | ---
7 |
8 | This section is all about the configuring (aka ricing) of your Hyprland experience.
9 | It links to other pages where necessary, and will walk you through:
10 |
11 | - The config file
12 | - Every option and function in it
13 | - Extending functionality via scripts
14 | - Some uncommon tips and tricks (e.g. switching layouts, disabling keybinds on-demand, etc)
15 |
16 | It also contains some sample configurations you can take inspiration from.
17 |
18 | Start with [the Start Page](./Start).
19 |
--------------------------------------------------------------------------------
/parser/data/variables.go:
--------------------------------------------------------------------------------
1 | package parser_data
2 |
3 | func FindVariableDefinitionInSection(sectionName, variableName string) *VariableDefinition {
4 | sec := FindSectionDefinitionByName(sectionName)
5 | if sec == nil {
6 | return nil
7 | }
8 | return sec.VariableDefinition(variableName)
9 | }
10 |
11 | type VariableDefinition struct {
12 | Name string
13 | Description string
14 | Type string
15 | Default string
16 | }
17 |
18 | func (v VariableDefinition) PrettyDefault() string {
19 | if v.Default == "[[Empty]]" {
20 | return "*(empty)*"
21 | }
22 | return v.Default
23 | }
24 |
25 | func (v VariableDefinition) GoType() string {
26 | switch v.Type {
27 | case "int":
28 | return "int"
29 | case "bool":
30 | return "bool"
31 | case "float", "floatvalue":
32 | return "float32"
33 | case "color":
34 | return "color.RGBA"
35 | case "vec2":
36 | return "[2]float32"
37 | case "MOD":
38 | return "[]ModKey"
39 | case "str", "string":
40 | return "string"
41 | case "gradient":
42 | return "GradientValue"
43 | case "font_weight":
44 | return "uint8"
45 | default:
46 | panic("unknown type: " + v.Type)
47 | }
48 |
49 | }
50 |
51 |
52 |
53 | func (v VariableDefinition) ParserTypeString() string {
54 | switch v.Type {
55 | case "int":
56 | return "Integer"
57 | case "bool":
58 | return "Bool"
59 | case "float":
60 | return "Float"
61 | case "color":
62 | return "Color"
63 | case "vec2":
64 | return "Vec2"
65 | case "MOD":
66 | return "Modmask"
67 | case "str", "string":
68 | return "String"
69 | case "gradient":
70 | return "Gradient"
71 | default:
72 | panic("unknown type: " + v.Type)
73 | }
74 | }
75 |
76 | func (v VariableDefinition) PascalCaseName() string {
77 | return toPascalCase(v.Name)
78 | }
79 |
--------------------------------------------------------------------------------
/parser/decode.go:
--------------------------------------------------------------------------------
1 | package parser
2 |
3 | import (
4 | "fmt"
5 | "reflect"
6 |
7 | parser_data "github.com/hyprland-community/hyprls/parser/data"
8 | )
9 |
10 | func (root Section) Decode() (Configuration, error) {
11 | config := Configuration{
12 | CustomVariables: make(map[string]string, 0),
13 | }
14 | root.WalkCustomVariables(func(v *CustomVariable) {
15 | config.CustomVariables[v.Key] = v.ValueRaw
16 | })
17 |
18 | for _, ass := range root.Assignments {
19 | def := parser_data.FindVariableDefinitionInSection("General", ass.Key)
20 | if def == nil {
21 | availableKeys := make([]string, 0)
22 | for _, v := range parser_data.FindSectionDefinitionByName("General").Variables {
23 | availableKeys = append(availableKeys, v.Name)
24 | }
25 | return Configuration{}, fmt.Errorf("unknown variable General > %s. Available keys are %v", ass.Key, availableKeys)
26 | }
27 | fmt.Printf("adding %s=%#v to .General", def.PascalCaseName(), ass.Value.GoValue())
28 | setValue(&config, def.PascalCaseName(), ass.Value.GoValue())
29 | }
30 |
31 | return config, nil
32 | }
33 |
34 | func setValue(obj any, field string, value any) {
35 | ref := reflect.ValueOf(obj)
36 |
37 | // if its a pointer, resolve its value
38 | if ref.Kind() == reflect.Ptr {
39 | ref = reflect.Indirect(ref)
40 | }
41 |
42 | if ref.Kind() == reflect.Interface {
43 | ref = ref.Elem()
44 | }
45 |
46 | // should double check we now have a struct (could still be anything)
47 | if ref.Kind() != reflect.Struct {
48 | panic("cannot setValue on a non-struct")
49 | }
50 |
51 | prop := ref.FieldByName(field)
52 | prop.Set(reflect.ValueOf(value))
53 | }
54 |
--------------------------------------------------------------------------------
/parser/decode_test.go:
--------------------------------------------------------------------------------
1 | package parser
2 |
3 | import (
4 | "testing"
5 |
6 | "github.com/davecgh/go-spew/spew"
7 | )
8 |
9 | func TestHighLevelParse(t *testing.T) {
10 | parsed, err := Parse(fixture)
11 | if err != nil {
12 | t.Errorf("Error while parsing: %s", err)
13 | }
14 |
15 | config, err := parsed.Decode()
16 | if err != nil {
17 | t.Errorf("Error while decoding: %s", err)
18 | return
19 | } else {
20 | spew.Dump(config)
21 | }
22 |
23 | t.Errorf("TestHighLevelParse not implemented")
24 | }
25 |
--------------------------------------------------------------------------------
/parser/fixtures/empty.hl:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyprland-community/hyprls/04c579ad66e7d1e841896fb28ef8b7bfcf67af40/parser/fixtures/empty.hl
--------------------------------------------------------------------------------
/parser/fixtures/test.hl:
--------------------------------------------------------------------------------
1 | #######################################################################################
2 | # AUTOGENERATED HYPR CONFIG.
3 | # PLEASE USE THE CONFIG PROVIDED IN THE GIT REPO /examples/hypr.conf AND EDIT IT,
4 | # OR EDIT THIS ONE ACCORDING TO THE WIKI INSTRUCTIONS.
5 | # #######################################################################################
6 |
7 | #
8 | # Please note not all available settings / options are set here.
9 | # For a full list, see the wiki
10 | #
11 |
12 | autogenerated = 0 # remove this line to remove the warning
13 |
14 |
15 | misc {
16 | # force_hypr_chan = true
17 | enable_swallow = true
18 | swallow_regex = ^kitty$
19 | }
20 |
21 | # See https://wiki.hyprland.org/Configuring/Monitors/
22 | source = ~/.config/hypr/monitors.conf
23 | monitor=,preferred,auto,1
24 |
25 | # See https://wiki.hyprland.org/Configuring/Keywords/ for more
26 |
27 | # Execute your favorite apps at launch
28 | exec-once = hyprpm reload -n & ~/.config/waybar/spotify-receiver & waybar & fcitx5 & discord & spotify & caprine & element-desktop & firefox & ckb-next --background & /usr/lib/polkit-kde-authentication-agent-1 & bash -c 'killall hyprpaper; hyprpaper' &
29 |
30 |
31 |
32 | # Source a file (multi-file configs)
33 | # source = ~/.config/hypr/myColors.conf
34 |
35 | # Some default env vars.
36 | env = XCURSOR_SIZE,24
37 |
38 | # For all categories, see https://wiki.hyprland.org/Configuring/Variables/
39 | input {
40 | kb_layout = fr
41 | kb_variant =
42 | kb_model =
43 | kb_options = compose:rwin
44 | kb_rules =
45 |
46 | follow_mouse = 1
47 |
48 | touchpad {
49 | natural_scroll = yes
50 | scroll_factor = 0.2
51 | }
52 |
53 |
54 | sensitivity = 0 # -1.0 - 1.0, 0 means no modification.
55 | }
56 |
57 | general {
58 | # See https://wiki.hyprland.org/Configuring/Variables/ for more
59 |
60 | gaps_in = 5
61 | gaps_out = 20
62 | border_size = 2
63 | col.active_border = rgba(ffc93391) rgb(ff0000) 45deg
64 | col.inactive_border = rgba(300adbab)
65 |
66 | layout = dwindle
67 | }
68 |
69 | decoration {
70 | # See https://wiki.hyprland.org/Configuring/Variables/ for more
71 |
72 | rounding = 10
73 |
74 | blur {
75 | enabled = true
76 | size = 10
77 | ignore_opacity = true
78 | xray = true
79 | passes = 2
80 | # noise = 0.2
81 | }
82 |
83 |
84 | active_opacity = 0.9
85 | inactive_opacity = 0.7
86 |
87 | # drop_shadow = yes
88 | # shadow_range = 4
89 | # shadow_render_power = 3
90 | # col.shadow = rgba(1a1a1aee)
91 | }
92 |
93 | animations {
94 | enabled = yes
95 |
96 | # Some default animations, see https://wiki.hyprland.org/Configuring/Animations/ for more
97 |
98 | bezier = myBezier, 0.05, 0.9, 0.1, 1.05
99 |
100 | animation = windows, 1, 7, myBezier
101 | animation = windowsOut, 1, 7, default, popin 80%
102 | animation = border, 1, 10, default
103 | animation = borderangle, 1, 8, default
104 | animation = fade, 1, 7, default
105 | animation = workspaces, 1, 6, default
106 | }
107 |
108 | dwindle {
109 | # See https://wiki.hyprland.org/Configuring/Dwindle-Layout/ for more
110 | pseudotile = yes # master switch for pseudotiling. Enabling is bound to mainMod + P in the keybinds section below
111 | preserve_split = yes # you probably want this
112 | force_split = 2
113 | }
114 |
115 | master {
116 | # See https://wiki.hyprland.org/Configuring/Master-Layout/ for more
117 | new_is_master = true
118 | }
119 |
120 | gestures {
121 | # See https://wiki.hyprland.org/Configuring/Variables/ for more
122 | workspace_swipe = on
123 | workspace_swipe_distance = 3000
124 | }
125 |
126 | # Example per-device config
127 | # See https://wiki.hyprland.org/Configuring/Keywords/#executing for more
128 | # device:epic-mouse-v1 {
129 | # sensitivity = -0.5
130 | # }
131 |
132 | # Example windowrule v1
133 | # windowrule = float, ^(kitty)$
134 | # Example windowrule v2
135 | # windowrulev2 = float,class:^(kitty)$,title:^(kitty)$
136 | # See https://wiki.hyprland.org/Configuring/Window-Rules/ for more
137 |
138 |
139 | # See https://wiki.hyprland.org/Configuring/Keywords/ for more
140 | $mainMod = SUPER
141 | $here = $HOME/.config/hypr
142 |
143 | # Example binds, see https://wiki.hyprland.org/Configuring/Binds/ for more
144 | bind = $mainMod SHIFT, Return, exec, warp-terminal
145 | bind = $mainMod, Return, exec, kitty
146 | bind = $mainMod, Q, killactive,
147 | bind = $mainMod SHIFT, C, exit,
148 | bind = $mainMod, E, exec, neovide
149 | bind = $mainMod, B, exec, firefox
150 | bind = $mainMod, P, exec, ~/.config/rofi/query
151 | bind = $mainMod SHIFT, Space, togglefloating,
152 | bind = $mainMod, D, exec, ~/.config/rofi/launchers/type-3/launcher.sh
153 | # bindr = $mainMod, Super_L, exec, pkill rofi || ~/.config/rofi/launchers/type-3/launcher.sh
154 | bind = $mainMod, Y, exec, rofimoji
155 |
156 | bind = $mainMod, V, togglesplit, # dwindle
157 | bind = $mainMod, lock, exec, waylock
158 |
159 | # Upgrade system
160 | bind = $mainMod, U, exec, [workspace 6] kitty --hold fish -c up
161 |
162 | # Move focus with mainMod + arrow keys
163 | bind = $mainMod, left, movefocus, l
164 | bind = $mainMod, h, movefocus, l
165 | bind = $mainMod, right, movefocus, r
166 | bind = $mainMod, l, movefocus, r
167 | bind = $mainMod, up, movefocus, u
168 | bind = $mainMod, k, movefocus, u
169 | bind = $mainMod, down, movefocus, d
170 | bind = $mainMod, j, movefocus, d
171 |
172 | # Switch workspaces with mainMod + [0-9]
173 | bind = $mainMod, ampersand, workspace, 1
174 | bind = $mainMod, eacute, workspace, 2
175 | bind = $mainMod, quotedbl, workspace, 3
176 | bind = $mainMod, apostrophe, workspace, 4
177 | bind = $mainMod, parenleft, workspace, 5
178 | bind = $mainMod, minus, workspace, 6
179 | bind = $mainMod, egrave, workspace, 7
180 | bind = $mainMod, underscore, workspace, 8
181 | bind = $mainMod, ccedilla, workspace, 9
182 | bind = $mainMod, agrave, workspace, 10
183 |
184 | # Move active window to a workspace with mainMod + SHIFT + [0-9]
185 | bind = $mainMod SHIFT, ampersand, movetoworkspace, 1
186 | bind = $mainMod SHIFT, eacute, movetoworkspace, 2
187 | bind = $mainMod SHIFT, quotedbl, movetoworkspace, 3
188 | bind = $mainMod SHIFT, apostrophe, movetoworkspace, 4
189 | bind = $mainMod SHIFT, parenleft, movetoworkspace, 5
190 | bind = $mainMod SHIFT, minus, movetoworkspace, 6
191 | bind = $mainMod SHIFT, egrave, movetoworkspace, 7
192 | bind = $mainMod SHIFT, underscore, movetoworkspace, 8
193 | bind = $mainMod SHIFT, ccedilla, movetoworkspace, 9
194 | bind = $mainMod SHIFT, agrave, movetoworkspace, 10
195 |
196 | # Move active workspace to other monitor
197 | bind = $mainMod CTRL, left, movecurrentworkspacetomonitor, l
198 | bind = $mainMod CTRL, right, movecurrentworkspacetomonitor, r
199 |
200 | # Scroll through existing workspaces with mainMod + scroll
201 | bind = $mainMod, mouse_down, workspace, e+1
202 | bind = $mainMod, mouse_up, workspace, e-1
203 |
204 | # Move/resize windows with mainMod + LMB/RMB and dragging
205 | bindm = $mainMod, mouse:272, movewindow
206 | bindm = $mainMod, mouse:273, resizewindow
207 |
208 | # Tabbed (grouped) windows
209 | bind = $mainMod, T, togglegroup
210 | bind = $mainMod SHIFT, tab, changegroupactive, b
211 | bind = $mainMod, tab, changegroupactive, f
212 |
213 | # Media keys
214 | binde = $mainMod, xf86monbrightnessup, exec, brillo -A 5
215 | binde = $mainMod, xf86monbrightnessdown, exec, brillo -U 5
216 | binde = , xf86monbrightnessup, exec, brillo -A 10
217 | binde = , xf86monbrightnessdown, exec, brillo -U 10
218 | binde = SHIFT, xf86monbrightnessup, exec, brillo -A 20
219 | binde = SHIFT, xf86monbrightnessdown, exec, brillo -U 20
220 |
221 | binde = , xf86audioraisevolume, exec, $here/volume_brightness.sh volume_up
222 | binde = , xf86audiolowervolume, exec, $here/volume_brightness.sh volume_down
223 | binde = , xf86audiomute, exec, $here/volume_brightness.sh volume_mute
224 |
225 | bind = , xf86audionext, exec, playerctl next
226 | bind = , xf86audioprev, exec, playerctl previous
227 | bind = , xf86audioplay, exec, playerctl play-pause
228 | bind = , xf86audiostop, exec, rofi-spotify --like-current
229 | bind = SHIFT, xf86audiostop, exec, rofi-spotify --add-to-playlist
230 |
231 | bind = , print, exec, hyprshot -m output
232 | bind = SHIFT, print, exec, hyprshot -m region
233 |
234 | bind = $mainMod ALT, u, exec, rofimoji -a unicode
235 |
236 | bind = $mainMod, F, fullscreen
237 | bind = $mainMod SHIFT, F, fullscreen, 1
238 |
239 | # Scratchpad
240 | bind = $mainMod SHIFT, equal, movetoworkspace, special
241 | bind = $mainMod, equal, togglespecialworkspace
242 |
243 | # Overview
244 | # bind = $mainMod, A, exec, hyprctl dispatch overview:toggle # (plugin: https://github.com/KZDKM/Hyprspace)
245 |
246 | bind = $mainMod, A, hyprexpo:expo, toggle # can be: toggle, off/disable or on/enable
247 |
248 | plugin {
249 | hyprexpo {
250 | columns = 3
251 | gap_size = 5
252 | bg_col = rgb(111111)
253 | workspace_method = first 1 # [center/first] [workspace] e.g. first 1 or center m+1
254 |
255 | enable_gesture = true # laptop touchpad, 4 fingers
256 | gesture_distance = 300 # how far is the "max"
257 | gesture_positive = true # positive = swipe down. Negative = swipe up.
258 | }
259 | }
260 |
261 | windowrulev2 = opacity 0.8 override 0.6 override,class:(kitty)
262 | windowrulev2 = opacity 0.8 override 0.6 override,class:(neovide)
263 | windowrulev2 = opacity 1 override 1 override,class:(obs)
264 | windowrulev2 = tile,class:(dev.warp.Warp)
265 |
266 | # Assigning apps to workspaces
267 | windowrulev2 = workspace 9 silent,class:(Spotify)
268 | windowrulev2 = workspace 10 silent,class:(Element)
269 | windowrulev2 = group set,class:(Element)
270 | windowrulev2 = workspace 10 silent,class:(Caprine)
271 | windowrulev2 = group set,class:(Caprine)
272 | windowrulev2 = workspace 10 silent,class:(discord)
273 | windowrulev2 = group set,class:(discord)
274 | windowrulev2 = workspace 3 silent,class:(^MATLAB),title:(^Figure \d: )
275 | windowrulev2 = workspace 3 silent,class:(Backend),title:(\[dev\])
276 |
277 | windowrulev2 = stayfocused,class:(Rofi)
278 |
279 | # Floating windows that shouldn't be
280 | windowrulev2 = tile,class:(qemu-system-x86_64)
281 | windowrulev2 = tile,class:(Pianoteq),title:(^Pianoteq)
282 | windowrulev2 = tile,class:(^MATLAB),title:(^Figure \d: )
283 |
284 | # kdwallet popups should steal focus
285 | windowrulev2 = stayfocused,class:(kwalletd5),title:(^KDE Wallet Service$)
286 |
--------------------------------------------------------------------------------
/parser/lowlevel_test.go:
--------------------------------------------------------------------------------
1 | package parser
2 |
3 | import (
4 | _ "embed"
5 | "encoding/json"
6 | "os"
7 | // "strings"
8 | "testing"
9 |
10 | // "github.com/andreyvit/diff"
11 | )
12 |
13 | //go:embed fixtures/test.hl
14 | var fixture string
15 |
16 | //go:embed fixtures/test.json
17 | var fixtureResultSnapshot string
18 |
19 | func TestLowlevelParse(t *testing.T) {
20 | parsed, err := Parse(fixture)
21 | if err != nil {
22 | t.Errorf("Error while parsing: %s", err)
23 | }
24 |
25 | contents, _ := json.MarshalIndent(parsed, "", " ")
26 | os.WriteFile("fixtures/test.json", contents, 0644)
27 | // if strings.TrimSpace(fixtureResultSnapshot) == "update" {
28 | // os.WriteFile("fixtures/test.json", contents, 0644)
29 | // } else {
30 | // if string(contents) != fixtureResultSnapshot {
31 | // t.Errorf("Parsed result does not match snapshot:\n%v", diff.LineDiff(string(contents), fixtureResultSnapshot))
32 | // }
33 | // }
34 | }
35 |
--------------------------------------------------------------------------------
/parser/utils.go:
--------------------------------------------------------------------------------
1 | package parser
2 |
3 | func not[T any](f func(T) bool) func(T) bool {
4 | return func(x T) bool {
5 | return !f(x)
6 | }
7 | }
8 |
--------------------------------------------------------------------------------
/renovate.json:
--------------------------------------------------------------------------------
1 | {
2 | "$schema": "https://docs.renovatebot.com/renovate-schema.json",
3 | "extends": ["config:recommended", "schedule:weekly"],
4 | "commitMessagePrefix": "⬆️ ",
5 | "rangeStrategy": "bump",
6 | "lockFileMaintenance": {
7 | "enabled": true,
8 | "automerge": true
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/shell.nix:
--------------------------------------------------------------------------------
1 | { pkgs ? (
2 | let
3 | inherit (builtins) fetchTree fromJSON readFile;
4 | inherit ((fromJSON (readFile ./flake.lock)).nodes) nixpkgs gomod2nix;
5 | in
6 | import (fetchTree nixpkgs.locked) {
7 | overlays = [
8 | (import "${fetchTree gomod2nix.locked}/overlay.nix")
9 | ];
10 | }
11 | )
12 | , mkGoEnv ? pkgs.mkGoEnv
13 | , gomod2nix ? pkgs.gomod2nix
14 | }:
15 |
16 | let
17 | goEnv = mkGoEnv { pwd = ./.; };
18 | in
19 | pkgs.mkShell {
20 | packages = [
21 | goEnv
22 | gomod2nix
23 | ] ++ (with pkgs; [
24 | just
25 | jq
26 | moreutils # sponge
27 | bun
28 | ]);
29 | }
30 |
--------------------------------------------------------------------------------
/state.go:
--------------------------------------------------------------------------------
1 | package hyprls
2 |
3 | import (
4 | "context"
5 | "os"
6 | "strings"
7 |
8 | "github.com/hyprland-community/hyprls/parser"
9 | parser_data "github.com/hyprland-community/hyprls/parser/data"
10 | "go.lsp.dev/protocol"
11 | "go.uber.org/zap"
12 | )
13 |
14 | var logger *zap.Logger
15 |
16 | var openedFiles = make(map[protocol.URI]string)
17 |
18 | type state struct {
19 | }
20 |
21 | func (h Handler) state(ctx context.Context) state {
22 | return ctx.Value("state").(state)
23 | }
24 |
25 | func parse(uri protocol.URI) (parser.Section, error) {
26 | contents, err := file(uri)
27 | if err != nil {
28 | return parser.Section{}, err
29 | }
30 |
31 | return parser.Parse(contents)
32 | }
33 |
34 | func currentSection(root parser.Section, position protocol.Position) *parser.Section {
35 | if !within(root.LSPRange(), position) {
36 | return nil
37 | }
38 |
39 | for _, section := range root.Subsections {
40 | sec := currentSection(section, position)
41 | if sec != nil {
42 | return sec
43 | }
44 | }
45 |
46 | return &root
47 | }
48 |
49 | func currentAssignment(root parser.Section, position protocol.Position) *parser_data.VariableDefinition {
50 | if !within(root.LSPRange(), position) {
51 | return nil
52 | }
53 |
54 | for _, assignment := range root.Assignments {
55 | if assignment.Position.Line == int(position.Line) {
56 | return parser_data.FindVariableDefinitionInSection(root.Name, assignment.Key)
57 | }
58 | }
59 |
60 | return nil
61 | }
62 |
63 | func within(rang protocol.Range, position protocol.Position) bool {
64 | if position.Line < rang.Start.Line || position.Line > rang.End.Line {
65 | return false
66 | }
67 |
68 | if position.Line == rang.Start.Line && position.Character < rang.Start.Character {
69 | return false
70 | }
71 |
72 | if position.Line == rang.End.Line && position.Character > rang.End.Character {
73 | return false
74 | }
75 |
76 | return true
77 | }
78 |
79 | func file(uri protocol.URI) (string, error) {
80 | if contents, ok := openedFiles[uri]; ok {
81 | return contents, nil
82 | }
83 |
84 | contents, err := os.ReadFile(uri.Filename())
85 | if err != nil {
86 | return "", err
87 | }
88 |
89 | openedFiles[uri] = string(contents)
90 | return string(contents), nil
91 | }
92 |
93 | func currentLine(uri protocol.URI, position protocol.Position) (string, error) {
94 | contents, err := file(uri)
95 | if err != nil {
96 | return "", err
97 | }
98 |
99 | lines := strings.Split(contents, "\n")
100 | return lines[position.Line], nil
101 | }
102 |
--------------------------------------------------------------------------------
/symbols.go:
--------------------------------------------------------------------------------
1 | package hyprls
2 |
3 | import (
4 | "context"
5 | "fmt"
6 |
7 | "github.com/hyprland-community/hyprls/parser"
8 | "go.lsp.dev/protocol"
9 | )
10 |
11 | func (h Handler) DocumentSymbol(ctx context.Context, params *protocol.DocumentSymbolParams) ([]interface{}, error) {
12 | document, err := parse(params.TextDocument.URI)
13 | if err != nil {
14 | return nil, fmt.Errorf("while parsing: %w", err)
15 | }
16 | symbols := make([]interface{}, 0)
17 | for _, symb := range gatherAllSymbols(document) {
18 | symbols = append(symbols, &symb)
19 | }
20 | return symbols, nil
21 | }
22 |
23 | func gatherAllSymbols(root parser.Section) []protocol.DocumentSymbol {
24 | symbols := make([]protocol.DocumentSymbol, 0)
25 | for _, variable := range root.Assignments {
26 | symbols = append(symbols, protocol.DocumentSymbol{
27 | Name: variable.Key,
28 | Kind: variable.Value.Kind.LSPSymbol(),
29 | Detail: variable.ValueRaw,
30 | Range: collapsedRange(variable.Position.LSP()),
31 | SelectionRange: collapsedRange(variable.Position.LSP()),
32 | })
33 | }
34 | for _, customVar := range root.Variables {
35 | symbols = append(symbols, protocol.DocumentSymbol{
36 | Name: "$" + customVar.Key,
37 | Kind: protocol.SymbolKindVariable,
38 | Detail: customVar.ValueRaw,
39 | Range: collapsedRange(customVar.Position.LSP()),
40 | SelectionRange: collapsedRange(customVar.Position.LSP()),
41 | })
42 | }
43 | for _, section := range root.Subsections {
44 | symbols = append(symbols, protocol.DocumentSymbol{
45 | Name: section.Name,
46 | Kind: protocol.SymbolKindNamespace,
47 | Range: section.LSPRange(),
48 | SelectionRange: section.LSPRange(),
49 | Children: gatherAllSymbols(section),
50 | })
51 | }
52 | return symbols
53 | }
54 |
--------------------------------------------------------------------------------
/sync.go:
--------------------------------------------------------------------------------
1 | package hyprls
2 |
3 | import (
4 | "context"
5 | "errors"
6 |
7 | "go.lsp.dev/protocol"
8 | "go.uber.org/zap"
9 | )
10 |
11 |
12 | func (h Handler) DidChange(ctx context.Context, params *protocol.DidChangeTextDocumentParams) error {
13 | logger.Debug("LSP:DidChange", zap.Any("params", params))
14 | openedFiles[params.TextDocument.URI] = params.ContentChanges[len(params.ContentChanges)-1].Text
15 | return nil
16 | }
17 |
18 | func (h Handler) DidClose(ctx context.Context, params *protocol.DidCloseTextDocumentParams) error {
19 | delete(openedFiles, params.TextDocument.URI)
20 | return nil
21 | }
22 |
23 | func (h Handler) DidOpen(ctx context.Context, params *protocol.DidOpenTextDocumentParams) error {
24 | file(params.TextDocument.URI)
25 | return nil
26 | }
27 |
28 | func (h Handler) DidSave(ctx context.Context, params *protocol.DidSaveTextDocumentParams) error {
29 | return errors.New("unimplemented")
30 | }
31 |
--------------------------------------------------------------------------------
/unimplemented.go:
--------------------------------------------------------------------------------
1 | package hyprls
2 |
3 | import (
4 | "context"
5 | "errors"
6 |
7 | "go.lsp.dev/protocol"
8 | )
9 |
10 | func (h Handler) Definition(ctx context.Context, params *protocol.DefinitionParams) ([]protocol.Location, error) {
11 | return []protocol.Location{}, errors.New("unimplemented")
12 | }
13 |
14 | func (h Handler) WorkDoneProgressCancel(ctx context.Context, params *protocol.WorkDoneProgressCancelParams) error {
15 | return errors.New("unimplemented")
16 | }
17 |
18 | func (h Handler) LogTrace(ctx context.Context, params *protocol.LogTraceParams) error {
19 | return errors.New("unimplemented")
20 | }
21 |
22 | func (h Handler) SetTrace(ctx context.Context, params *protocol.SetTraceParams) error {
23 | return errors.New("unimplemented")
24 | }
25 |
26 | func (h Handler) CodeAction(ctx context.Context, params *protocol.CodeActionParams) ([]protocol.CodeAction, error) {
27 | return nil, errors.New("unimplemented")
28 | }
29 |
30 | func (h Handler) CodeLens(ctx context.Context, params *protocol.CodeLensParams) ([]protocol.CodeLens, error) {
31 | return nil, errors.New("unimplemented")
32 | }
33 |
34 | func (h Handler) CodeLensResolve(ctx context.Context, params *protocol.CodeLens) (*protocol.CodeLens, error) {
35 | return nil, errors.New("unimplemented")
36 | }
37 |
38 | func (h Handler) Declaration(ctx context.Context, params *protocol.DeclarationParams) ([]protocol.Location, error) {
39 | return nil, errors.New("unimplemented")
40 | }
41 |
42 | func (h Handler) DidChangeConfiguration(ctx context.Context, params *protocol.DidChangeConfigurationParams) error {
43 | return errors.New("unimplemented")
44 | }
45 |
46 | func (h Handler) DidChangeWatchedFiles(ctx context.Context, params *protocol.DidChangeWatchedFilesParams) error {
47 | return errors.New("unimplemented")
48 | }
49 |
50 | func (h Handler) DidChangeWorkspaceFolders(ctx context.Context, params *protocol.DidChangeWorkspaceFoldersParams) error {
51 | return errors.New("unimplemented")
52 | }
53 |
54 | func (h Handler) DocumentHighlight(ctx context.Context, params *protocol.DocumentHighlightParams) ([]protocol.DocumentHighlight, error) {
55 | return nil, errors.New("unimplemented")
56 | }
57 |
58 | func (h Handler) DocumentLink(ctx context.Context, params *protocol.DocumentLinkParams) ([]protocol.DocumentLink, error) {
59 | return nil, errors.New("unimplemented")
60 | }
61 |
62 | func (h Handler) DocumentLinkResolve(ctx context.Context, params *protocol.DocumentLink) (*protocol.DocumentLink, error) {
63 | return nil, errors.New("unimplemented")
64 | }
65 |
66 | func (h Handler) ExecuteCommand(ctx context.Context, params *protocol.ExecuteCommandParams) (interface{}, error) {
67 | return nil, errors.New("unimplemented")
68 | }
69 |
70 | func (h Handler) FoldingRanges(ctx context.Context, params *protocol.FoldingRangeParams) ([]protocol.FoldingRange, error) {
71 | return nil, errors.New("unimplemented")
72 | }
73 |
74 | func (h Handler) Formatting(ctx context.Context, params *protocol.DocumentFormattingParams) ([]protocol.TextEdit, error) {
75 | return nil, errors.New("unimplemented")
76 | }
77 |
78 | func (h Handler) Implementation(ctx context.Context, params *protocol.ImplementationParams) ([]protocol.Location, error) {
79 | return nil, errors.New("unimplemented")
80 | }
81 |
82 | func (h Handler) OnTypeFormatting(ctx context.Context, params *protocol.DocumentOnTypeFormattingParams) ([]protocol.TextEdit, error) {
83 | return nil, errors.New("unimplemented")
84 | }
85 |
86 | func (h Handler) PrepareRename(ctx context.Context, params *protocol.PrepareRenameParams) (*protocol.Range, error) {
87 | return nil, errors.New("unimplemented")
88 | }
89 |
90 | func (h Handler) RangeFormatting(ctx context.Context, params *protocol.DocumentRangeFormattingParams) ([]protocol.TextEdit, error) {
91 | return nil, errors.New("unimplemented")
92 | }
93 |
94 | func (h Handler) References(ctx context.Context, params *protocol.ReferenceParams) ([]protocol.Location, error) {
95 | return nil, errors.New("unimplemented")
96 | }
97 |
98 | func (h Handler) Rename(ctx context.Context, params *protocol.RenameParams) (*protocol.WorkspaceEdit, error) {
99 | return nil, errors.New("unimplemented")
100 | }
101 |
102 | func (h Handler) SignatureHelp(ctx context.Context, params *protocol.SignatureHelpParams) (*protocol.SignatureHelp, error) {
103 | return nil, errors.New("unimplemented")
104 | }
105 |
106 | func (h Handler) Symbols(ctx context.Context, params *protocol.WorkspaceSymbolParams) ([]protocol.SymbolInformation, error) {
107 | return nil, errors.New("unimplemented")
108 | }
109 |
110 | func (h Handler) TypeDefinition(ctx context.Context, params *protocol.TypeDefinitionParams) ([]protocol.Location, error) {
111 | return nil, errors.New("unimplemented")
112 | }
113 |
114 | func (h Handler) WillSave(ctx context.Context, params *protocol.WillSaveTextDocumentParams) error {
115 | return errors.New("unimplemented")
116 | }
117 |
118 | func (h Handler) WillSaveWaitUntil(ctx context.Context, params *protocol.WillSaveTextDocumentParams) ([]protocol.TextEdit, error) {
119 | return nil, errors.New("unimplemented")
120 | }
121 |
122 | func (h Handler) ShowDocument(ctx context.Context, params *protocol.ShowDocumentParams) (*protocol.ShowDocumentResult, error) {
123 | return nil, errors.New("unimplemented")
124 | }
125 |
126 | func (h Handler) WillCreateFiles(ctx context.Context, params *protocol.CreateFilesParams) (*protocol.WorkspaceEdit, error) {
127 | return nil, errors.New("unimplemented")
128 | }
129 |
130 | func (h Handler) DidCreateFiles(ctx context.Context, params *protocol.CreateFilesParams) error {
131 | return errors.New("unimplemented")
132 | }
133 |
134 | func (h Handler) WillRenameFiles(ctx context.Context, params *protocol.RenameFilesParams) (*protocol.WorkspaceEdit, error) {
135 | return nil, errors.New("unimplemented")
136 | }
137 |
138 | func (h Handler) DidRenameFiles(ctx context.Context, params *protocol.RenameFilesParams) error {
139 | return errors.New("unimplemented")
140 | }
141 |
142 | func (h Handler) WillDeleteFiles(ctx context.Context, params *protocol.DeleteFilesParams) (*protocol.WorkspaceEdit, error) {
143 | return nil, errors.New("unimplemented")
144 | }
145 |
146 | func (h Handler) DidDeleteFiles(ctx context.Context, params *protocol.DeleteFilesParams) error {
147 | return errors.New("unimplemented")
148 | }
149 |
150 | func (h Handler) CodeLensRefresh(ctx context.Context) error {
151 | return errors.New("unimplemented")
152 | }
153 |
154 | func (h Handler) PrepareCallHierarchy(ctx context.Context, params *protocol.CallHierarchyPrepareParams) ([]protocol.CallHierarchyItem, error) {
155 | return nil, errors.New("unimplemented")
156 | }
157 |
158 | func (h Handler) IncomingCalls(ctx context.Context, params *protocol.CallHierarchyIncomingCallsParams) ([]protocol.CallHierarchyIncomingCall, error) {
159 | return nil, errors.New("unimplemented")
160 | }
161 |
162 | func (h Handler) OutgoingCalls(ctx context.Context, params *protocol.CallHierarchyOutgoingCallsParams) ([]protocol.CallHierarchyOutgoingCall, error) {
163 | return nil, errors.New("unimplemented")
164 | }
165 |
166 | func (h Handler) SemanticTokensFull(ctx context.Context, params *protocol.SemanticTokensParams) (*protocol.SemanticTokens, error) {
167 | return nil, errors.New("unimplemented")
168 | }
169 |
170 | func (h Handler) SemanticTokensFullDelta(ctx context.Context, params *protocol.SemanticTokensDeltaParams) (interface{}, error) {
171 | return nil, errors.New("unimplemented")
172 | }
173 |
174 | func (h Handler) SemanticTokensRange(ctx context.Context, params *protocol.SemanticTokensRangeParams) (*protocol.SemanticTokens, error) {
175 | return nil, errors.New("unimplemented")
176 | }
177 |
178 | func (h Handler) SemanticTokensRefresh(ctx context.Context) error {
179 | return errors.New("unimplemented")
180 | }
181 |
182 | func (h Handler) LinkedEditingRange(ctx context.Context, params *protocol.LinkedEditingRangeParams) (*protocol.LinkedEditingRanges, error) {
183 | return nil, errors.New("unimplemented")
184 | }
185 |
186 | func (h Handler) Moniker(ctx context.Context, params *protocol.MonikerParams) ([]protocol.Moniker, error) {
187 | return nil, errors.New("unimplemented")
188 | }
189 |
190 | func (h Handler) Request(ctx context.Context, method string, params interface{}) (interface{}, error) {
191 | return nil, errors.New("unimplemented")
192 | }
193 |
--------------------------------------------------------------------------------
/utils.go:
--------------------------------------------------------------------------------
1 | package hyprls
2 |
3 | import "go.lsp.dev/protocol"
4 |
5 | func collapsedRange(position protocol.Position) protocol.Range {
6 | return protocol.Range{
7 | Start: position,
8 | End: position,
9 | }
10 | }
11 |
12 |
--------------------------------------------------------------------------------
/vscode-extension-pack/.gitignore:
--------------------------------------------------------------------------------
1 | # Created by https://www.toptal.com/developers/gitignore/api/node
2 | # Edit at https://www.toptal.com/developers/gitignore?templates=node
3 |
4 | ### Node ###
5 | # Logs
6 | logs
7 | *.log
8 | npm-debug.log*
9 | yarn-debug.log*
10 | yarn-error.log*
11 | lerna-debug.log*
12 | .pnpm-debug.log*
13 |
14 | # Diagnostic reports (https://nodejs.org/api/report.html)
15 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
16 |
17 | # Runtime data
18 | pids
19 | *.pid
20 | *.seed
21 | *.pid.lock
22 |
23 | # Directory for instrumented libs generated by jscoverage/JSCover
24 | lib-cov
25 |
26 | # Coverage directory used by tools like istanbul
27 | coverage
28 | *.lcov
29 |
30 | # nyc test coverage
31 | .nyc_output
32 |
33 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
34 | .grunt
35 |
36 | # Bower dependency directory (https://bower.io/)
37 | bower_components
38 |
39 | # node-waf configuration
40 | .lock-wscript
41 |
42 | # Compiled binary addons (https://nodejs.org/api/addons.html)
43 | build/Release
44 |
45 | # Dependency directories
46 | node_modules/
47 | jspm_packages/
48 |
49 | # Snowpack dependency directory (https://snowpack.dev/)
50 | web_modules/
51 |
52 | # TypeScript cache
53 | *.tsbuildinfo
54 |
55 | # Optional npm cache directory
56 | .npm
57 |
58 | # Optional eslint cache
59 | .eslintcache
60 |
61 | # Optional stylelint cache
62 | .stylelintcache
63 |
64 | # Microbundle cache
65 | .rpt2_cache/
66 | .rts2_cache_cjs/
67 | .rts2_cache_es/
68 | .rts2_cache_umd/
69 |
70 | # Optional REPL history
71 | .node_repl_history
72 |
73 | # Output of 'npm pack'
74 | *.tgz
75 |
76 | # Yarn Integrity file
77 | .yarn-integrity
78 |
79 | # dotenv environment variable files
80 | .env
81 | .env.development.local
82 | .env.test.local
83 | .env.production.local
84 | .env.local
85 |
86 | # parcel-bundler cache (https://parceljs.org/)
87 | .cache
88 | .parcel-cache
89 |
90 | # Next.js build output
91 | .next
92 | out
93 |
94 | # Nuxt.js build / generate output
95 | .nuxt
96 | dist
97 |
98 | # Gatsby files
99 | .cache/
100 | # Comment in the public line in if your project uses Gatsby and not Next.js
101 | # https://nextjs.org/blog/next-9-1#public-directory-support
102 | # public
103 |
104 | # vuepress build output
105 | .vuepress/dist
106 |
107 | # vuepress v2.x temp and cache directory
108 | .temp
109 |
110 | # Docusaurus cache and generated files
111 | .docusaurus
112 |
113 | # Serverless directories
114 | .serverless/
115 |
116 | # FuseBox cache
117 | .fusebox/
118 |
119 | # DynamoDB Local files
120 | .dynamodb/
121 |
122 | # TernJS port file
123 | .tern-port
124 |
125 | # Stores VSCode versions used for testing VSCode extensions
126 | .vscode-test
127 |
128 | # yarn v2
129 | .yarn/cache
130 | .yarn/unplugged
131 | .yarn/build-state.yml
132 | .yarn/install-state.gz
133 | .pnp.*
134 |
135 | ### Node Patch ###
136 | # Serverless Webpack directories
137 | .webpack/
138 |
139 | # Optional stylelint cache
140 |
141 | # SvelteKit build / generate output
142 | .svelte-kit
143 |
144 | # End of https://www.toptal.com/developers/gitignore/api/node
145 |
--------------------------------------------------------------------------------
/vscode-extension-pack/.vscode/launch.json:
--------------------------------------------------------------------------------
1 | // A launch configuration that launches the extension inside a new window
2 | // Use IntelliSense to learn about possible attributes.
3 | // Hover to view descriptions of existing attributes.
4 | // For more information, visit: https://go.microsoft.com/fwlink/?linkid=830387
5 | {
6 | "version": "0.2.0",
7 | "configurations": [
8 | {
9 | "name": "Extension",
10 | "type": "extensionHost",
11 | "request": "launch",
12 | "args": [
13 | "--extensionDevelopmentPath=${workspaceFolder}"
14 | ]
15 | }
16 | ]
17 | }
--------------------------------------------------------------------------------
/vscode-extension-pack/.vscodeignore:
--------------------------------------------------------------------------------
1 | .vscode/**
2 | .vscode-test/**
3 | .gitignore
4 | vsc-extension-quickstart.md
5 |
--------------------------------------------------------------------------------
/vscode-extension-pack/CHANGELOG.md:
--------------------------------------------------------------------------------
1 | # Change Log
2 |
3 | All notable changes to the "hyprland" extension pack will be documented in this file.
4 |
5 | Check [Keep a Changelog](http://keepachangelog.com/) for recommendations on how to structure this file.
6 |
7 | ## [Unreleased]
8 |
9 | - Initial release
10 |
--------------------------------------------------------------------------------
/vscode-extension-pack/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice (including the next
11 | paragraph) shall be included in all copies or substantial portions of the
12 | Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
17 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 | OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/vscode-extension-pack/README.md:
--------------------------------------------------------------------------------
1 | # hyprland extension pack
2 |
3 | Installs syntax highlighting and LSP support for [Hyprland](https://hyprland.org) config files
4 |
5 |
--------------------------------------------------------------------------------
/vscode-extension-pack/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyprland-community/hyprls/04c579ad66e7d1e841896fb28ef8b7bfcf67af40/vscode-extension-pack/bun.lockb
--------------------------------------------------------------------------------
/vscode-extension-pack/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyprland-community/hyprls/04c579ad66e7d1e841896fb28ef8b7bfcf67af40/vscode-extension-pack/icon.png
--------------------------------------------------------------------------------
/vscode-extension-pack/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hyprland",
3 | "displayName": "Hyprland",
4 | "description": "Extension pack for syntax highlighting and rich IDE support in Hyprland configuration files",
5 | "version": "0.1.2",
6 | "engines": {
7 | "vscode": "^1.100.2"
8 | },
9 | "categories": [
10 | "Extension Packs"
11 | ],
12 | "extensionPack": [
13 | "ewen-lbh.vscode-hyprls",
14 | "fireblast.hyprlang-vscode"
15 | ],
16 | "devDependencies": {
17 | "@vscode/vsce": "^3.4.2"
18 | },
19 | "repository": "https://github.com/ewen-lbh/hyprls/tree/main/vscode-extension-pack",
20 | "license": "MIT",
21 | "publisher": "ewen-lbh",
22 | "icon": "icon.png"
23 | }
24 |
--------------------------------------------------------------------------------
/vscode-extension-pack/package.json.bak:
--------------------------------------------------------------------------------
1 | {
2 | "name": "hyprland",
3 | "displayName": "hyprland",
4 | "description": "Extension pack for syntax highlighting and rich IDE support in Hyprland configuration files",
5 | "version": "0.0.1",
6 | "engines": {
7 | "vscode": "^1.88.0"
8 | },
9 | "categories": [
10 | "Extension Packs"
11 | ],
12 | "extensionPack": [
13 | "publisher.extensionName"
14 | ],
15 | "devDependencies": {
16 | "@vscode/vsce": "^2.26.0"
17 | }
18 | }
19 |
--------------------------------------------------------------------------------
/vscode-extension-pack/vsc-extension-quickstart.md:
--------------------------------------------------------------------------------
1 | # Welcome to your VS Code Extension Pack
2 |
3 | ## What's in the folder
4 |
5 | * This folder contains all of the files necessary for your extension pack.
6 | * `package.json` - this is the manifest file that defines the list of extensions of the extension pack.
7 |
8 | ## Get up and running straight away
9 |
10 | * Press `F5` to open a new window with your extension loaded.
11 | * Open `Extensions Viewlet` and check your extensions are installed.
12 |
13 | ## Make changes
14 |
15 | * You can relaunch the extension from the debug toolbar after making changes to the files listed above.
16 | * You can also reload (`Ctrl+R` or `Cmd+R` on Mac) the VS Code window with your extension to load your changes.
17 |
18 | ## Install your extension
19 |
20 | * To start using your extension with Visual Studio Code copy it into the `/.vscode/extensions` folder and restart Code.
21 | * To share your extension with the world, read on https://code.visualstudio.com/docs about publishing an extension.
22 |
--------------------------------------------------------------------------------
/vscode/.eslintignore:
--------------------------------------------------------------------------------
1 | node_modules/**
2 | client/node_modules/**
3 | client/out/**
4 | server/node_modules/**
5 | server/out/**
--------------------------------------------------------------------------------
/vscode/.eslintrc.js:
--------------------------------------------------------------------------------
1 | /**@type {import('eslint').Linter.Config} */
2 | // eslint-disable-next-line no-undef
3 | module.exports = {
4 | root: true,
5 | parser: "@typescript-eslint/parser",
6 | plugins: ["@typescript-eslint"],
7 | extends: ["eslint:recommended", "plugin:@typescript-eslint/recommended"],
8 | rules: {
9 | semi: "off",
10 | "@typescript-eslint/no-unused-vars": 0,
11 | "@typescript-eslint/no-explicit-any": 0,
12 | "@typescript-eslint/explicit-module-boundary-types": 0,
13 | "@typescript-eslint/no-non-null-assertion": 0,
14 | },
15 | }
16 |
--------------------------------------------------------------------------------
/vscode/.gitignore:
--------------------------------------------------------------------------------
1 | out
2 | node_modules
3 | .vscode-test
4 | # Created by https://www.toptal.com/developers/gitignore/api/node
5 | # Edit at https://www.toptal.com/developers/gitignore?templates=node
6 |
7 | ### Node ###
8 | # Logs
9 | logs
10 | *.log
11 | npm-debug.log*
12 | yarn-debug.log*
13 | yarn-error.log*
14 | lerna-debug.log*
15 | .pnpm-debug.log*
16 |
17 | # Diagnostic reports (https://nodejs.org/api/report.html)
18 | report.[0-9]*.[0-9]*.[0-9]*.[0-9]*.json
19 |
20 | # Runtime data
21 | pids
22 | *.pid
23 | *.seed
24 | *.pid.lock
25 |
26 | # Directory for instrumented libs generated by jscoverage/JSCover
27 | lib-cov
28 |
29 | # Coverage directory used by tools like istanbul
30 | coverage
31 | *.lcov
32 |
33 | # nyc test coverage
34 | .nyc_output
35 |
36 | # Grunt intermediate storage (https://gruntjs.com/creating-plugins#storing-task-files)
37 | .grunt
38 |
39 | # Bower dependency directory (https://bower.io/)
40 | bower_components
41 |
42 | # node-waf configuration
43 | .lock-wscript
44 |
45 | # Compiled binary addons (https://nodejs.org/api/addons.html)
46 | build/Release
47 |
48 | # Dependency directories
49 | node_modules/
50 | jspm_packages/
51 |
52 | # Snowpack dependency directory (https://snowpack.dev/)
53 | web_modules/
54 |
55 | # TypeScript cache
56 | *.tsbuildinfo
57 |
58 | # Optional npm cache directory
59 | .npm
60 |
61 | # Optional eslint cache
62 | .eslintcache
63 |
64 | # Optional stylelint cache
65 | .stylelintcache
66 |
67 | # Microbundle cache
68 | .rpt2_cache/
69 | .rts2_cache_cjs/
70 | .rts2_cache_es/
71 | .rts2_cache_umd/
72 |
73 | # Optional REPL history
74 | .node_repl_history
75 |
76 | # Output of 'npm pack'
77 | *.tgz
78 |
79 | # Yarn Integrity file
80 | .yarn-integrity
81 |
82 | # dotenv environment variable files
83 | .env
84 | .env.development.local
85 | .env.test.local
86 | .env.production.local
87 | .env.local
88 |
89 | # parcel-bundler cache (https://parceljs.org/)
90 | .cache
91 | .parcel-cache
92 |
93 | # Next.js build output
94 | .next
95 | out
96 |
97 | # Nuxt.js build / generate output
98 | .nuxt
99 | dist
100 |
101 | # Gatsby files
102 | .cache/
103 | # Comment in the public line in if your project uses Gatsby and not Next.js
104 | # https://nextjs.org/blog/next-9-1#public-directory-support
105 | # public
106 |
107 | # vuepress build output
108 | .vuepress/dist
109 |
110 | # vuepress v2.x temp and cache directory
111 | .temp
112 |
113 | # Docusaurus cache and generated files
114 | .docusaurus
115 |
116 | # Serverless directories
117 | .serverless/
118 |
119 | # FuseBox cache
120 | .fusebox/
121 |
122 | # DynamoDB Local files
123 | .dynamodb/
124 |
125 | # TernJS port file
126 | .tern-port
127 |
128 | # Stores VSCode versions used for testing VSCode extensions
129 | .vscode-test
130 |
131 | # yarn v2
132 | .yarn/cache
133 | .yarn/unplugged
134 | .yarn/build-state.yml
135 | .yarn/install-state.gz
136 | .pnp.*
137 |
138 | ### Node Patch ###
139 | # Serverless Webpack directories
140 | .webpack/
141 |
142 | # Optional stylelint cache
143 |
144 | # SvelteKit build / generate output
145 | .svelte-kit
146 |
147 | # End of https://www.toptal.com/developers/gitignore/api/node
148 |
--------------------------------------------------------------------------------
/vscode/.vscodeignore:
--------------------------------------------------------------------------------
1 | .vscode/**
2 | **/*.ts
3 | **/*.map
4 | .gitignore
5 | **/tsconfig.json
6 | **/tsconfig.base.json
7 | contributing.md
8 | .travis.yml
9 | pkg/
10 | scripts/
11 |
--------------------------------------------------------------------------------
/vscode/LICENSE:
--------------------------------------------------------------------------------
1 | MIT License
2 |
3 | Permission is hereby granted, free of charge, to any person obtaining a copy
4 | of this software and associated documentation files (the "Software"), to deal
5 | in the Software without restriction, including without limitation the rights
6 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7 | copies of the Software, and to permit persons to whom the Software is furnished
8 | to do so, subject to the following conditions:
9 |
10 | The above copyright notice and this permission notice (including the next
11 | paragraph) shall be included in all copies or substantial portions of the
12 | Software.
13 |
14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
15 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
16 | FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS
17 | OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
18 | WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF
19 | OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
20 |
--------------------------------------------------------------------------------
/vscode/README.md:
--------------------------------------------------------------------------------
1 | # VSCode Extension for Hyprland configuraiton files
2 |
3 | ## Installation
4 |
5 | Requires [installing `hyprls`](https://github.com/ewen-lbh/hyprls) and having in on your PATH.
6 |
--------------------------------------------------------------------------------
/vscode/bun.lockb:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyprland-community/hyprls/04c579ad66e7d1e841896fb28ef8b7bfcf67af40/vscode/bun.lockb
--------------------------------------------------------------------------------
/vscode/icon.png:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyprland-community/hyprls/04c579ad66e7d1e841896fb28ef8b7bfcf67af40/vscode/icon.png
--------------------------------------------------------------------------------
/vscode/package.json:
--------------------------------------------------------------------------------
1 | {
2 | "name": "vscode-hyprls",
3 | "description": "VSCode extension for HyprLS",
4 | "author": "Gwenn Le Bihan ",
5 | "license": "MIT",
6 | "version": "0.7.0",
7 | "repository": {
8 | "type": "git",
9 | "url": "https://github.com/hyprland-community/hyprls"
10 | },
11 | "publisher": "ewen-lbh",
12 | "categories": [],
13 | "keywords": [],
14 | "engines": {
15 | "vscode": "^1.100.2"
16 | },
17 | "contributes": {
18 | "languages": [
19 | {
20 | "id": "hyprlang",
21 | "aliases": [
22 | "HyprLang"
23 | ],
24 | "extensions": [
25 | ".hl"
26 | ],
27 | "filenamePatterns": [
28 | "hypr*.conf"
29 | ]
30 | }
31 | ],
32 | "commands": [
33 | {
34 | "category": "HyprLS",
35 | "title": "Restart language server",
36 | "command": "vscode-hyprls.restart-lsp"
37 | }
38 | ]
39 | },
40 | "activationEvents": [
41 | "onLanguage:hyprlang"
42 | ],
43 | "main": "./out/extension",
44 | "scripts": {
45 | "vscode:prepublish": "npm run compile",
46 | "compile": "tsc -b",
47 | "watch": "tsc -b -w",
48 | "lint": "eslint ./src --ext .ts,.tsx",
49 | "test": "sh ./scripts/e2e.sh"
50 | },
51 | "devDependencies": {
52 | "@ortfo/db": "^1.6.1",
53 | "@types/mocha": "^10.0.10",
54 | "@types/node": "^22.15.29",
55 | "@types/vscode": "^1.100.0",
56 | "@typescript-eslint/eslint-plugin": "^8.32.1",
57 | "@typescript-eslint/parser": "^8.32.1",
58 | "@vscode/test-electron": "^2.5.2",
59 | "@vscode/vsce": "^3.4.2",
60 | "eslint": "^9.28.0",
61 | "mocha": "^11.5.0",
62 | "typescript": "^5.8.3"
63 | },
64 | "dependencies": {
65 | "vscode-languageclient": "^9.0.1"
66 | },
67 | "icon": "icon.png",
68 | "displayName": "HyprLS"
69 | }
70 |
--------------------------------------------------------------------------------
/vscode/scripts/e2e.sh:
--------------------------------------------------------------------------------
1 | #!/usr/bin/env bash
2 |
3 | export CODE_TESTS_PATH="$(pwd)/client/out/test"
4 | export CODE_TESTS_WORKSPACE="$(pwd)/client/testFixture"
5 |
6 | node "$(pwd)/client/out/test/runTest"
--------------------------------------------------------------------------------
/vscode/src/extension.ts:
--------------------------------------------------------------------------------
1 | /* --------------------------------------------------------------------------------------------
2 | * Copyright (c) Microsoft Corporation. All rights reserved.
3 | * Licensed under the MIT License. See License.txt in the project root for license information.
4 | * ------------------------------------------------------------------------------------------ */
5 |
6 | import { commands, ExtensionContext, workspace } from "vscode"
7 |
8 | import {
9 | LanguageClient,
10 | LanguageClientOptions,
11 | ServerOptions,
12 | TransportKind,
13 | } from "vscode-languageclient/node"
14 |
15 | let client: LanguageClient
16 |
17 | export function activate(context: ExtensionContext) {
18 | const serverModule = "hyprls"
19 |
20 | // If the extension is launched in debug mode then the debug server options are used
21 | // Otherwise the run options are used
22 | const serverOptions: ServerOptions = {
23 | run: {
24 | command: serverModule,
25 | transport: TransportKind.stdio,
26 | },
27 | debug: {
28 | command: "/home/uwun/projects/hyprls/hyprlang-lsp",
29 | transport: TransportKind.stdio,
30 | },
31 | }
32 |
33 | // Options to control the language client
34 | const clientOptions: LanguageClientOptions = {
35 | // Register the server for plain text documents
36 | documentSelector: [{ scheme: "file", language: "hyprlang" }],
37 | outputChannelName: "HyprLS",
38 | synchronize: {
39 | fileEvents: workspace.createFileSystemWatcher("*.hl"),
40 | },
41 | }
42 |
43 | context.subscriptions.push(
44 | commands.registerCommand("vscode-hyprls.restart-lsp", () => {
45 | client.restart()
46 | })
47 | )
48 |
49 | // Create the language client and start the client.
50 | client = new LanguageClient("hyprlang", "Hypr", serverOptions, clientOptions)
51 |
52 | // Start the client. This will also launch the server
53 | client.start()
54 | }
55 |
56 | export function deactivate(): Thenable | undefined {
57 | if (!client) {
58 | return undefined
59 | }
60 | return client.stop()
61 | }
62 |
--------------------------------------------------------------------------------
/vscode/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "module": "commonjs",
4 | "target": "es2020",
5 | "lib": ["es2020"],
6 | "outDir": "out",
7 | "rootDir": "src",
8 | "sourceMap": true,
9 | "skipLibCheck": true,
10 | "esModuleInterop": true
11 | },
12 | "include": ["src"],
13 | "exclude": ["node_modules", ".vscode-test"]
14 | }
15 |
--------------------------------------------------------------------------------
/vscode/vscode-hyprlang-0.0.1.vsix:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/hyprland-community/hyprls/04c579ad66e7d1e841896fb28ef8b7bfcf67af40/vscode/vscode-hyprlang-0.0.1.vsix
--------------------------------------------------------------------------------