# AnotherEditor

AnotherEditor is an early Go + Qt6 desktop editor built around QScintilla via miqt's `qscintilla6` bindings.

## Requirements

- Go 1.22 or newer
- Qt6 development files discoverable through `pkg-config`
- QScintilla for Qt6
- macOS Homebrew layout at `/opt/homebrew/opt/qscintilla2` for the first supported build path

The repository includes `pkgconfig/QScintilla6.pc` as a local shim for Homebrew's QScintilla package. `make` exports `PKG_CONFIG_PATH` so miqt's QScintilla bindings can resolve it.

Release builds run `scripts/ensure_qscintilla6.sh` before compiling. The script writes a target-specific `pkgconfig/<target>/QScintilla6.pc`, validates that `qsciabstractapis.h` is reachable, installs Linux distro packages when available, and can build QScintilla from source for cross-build containers. It searches common MXE, osxcross, `/opt/Qt`, `/opt/qt6`, and `/usr/local/qt6` qmake layouts; set `QSCINTILLA_QMAKE` if a runner's target qmake uses a custom path.

## Build

```sh
make deps-check
make test
make build
```

Run the app with:

```sh
make run
```

Run the headless Qt startup smoke test with:

```sh
make smoke-test
```

Open files directly with:

```sh
bin/anothereditor file1.go file2.md
```

Remote files can be opened from `File > Open Remote...` with `ftp://` or `sftp://` URLs, or selected through `File > Browse Remote...`. AnotherEditor prompts for the password or key passphrase when opening a remote file, keeps the session in memory for save-back, and can optionally remember successful secrets through the configured Git credential helper. Remote file reads, directory listings, and ordinary saves run as cancellable background transfers with status-bar progress; failed reads, listings, and saves can reconnect and retry once. Remote saves compare server metadata against the version opened or last saved and ask before overwriting a changed server copy; writes are staged through a same-directory temporary file and renamed into place to reduce partial target updates. `File > Remote Credential Status` shows whether Git is available and which credential helpers are configured; `File > Remote Credential Diagnostic...` checks a specific remote URL for helper lookup status. Neither view displays secrets. SFTP verifies host keys through `~/.ssh/known_hosts` by default and can add a host key after user confirmation on first connection; URLs may include `identity_file=...`, `known_hosts=...`, or explicit `insecure=1` for development hosts. FTP URLs may include `tls=explicit` or `tls=implicit`.

Build a local release zip with:

```sh
make release-local
```

Install Linux desktop and MIME integration for the current user with:

```sh
make build
make install-linux-integration
```

Build and install a local macOS app bundle for Finder Open With registration with:

```sh
make macos-app-bundle
make install-macos-finder-integration
```

CI macOS releases are intentionally unsigned and do not run notarization. Local app bundle signing remains a manual developer option when running `make macos-app-bundle`.

Windows installer packaging is defined in `packaging/windows/anothereditor.iss.in` and can be built with `make windows-installer` on a Windows machine with Inno Setup and PowerShell. CI Windows releases are unsigned zip bundles.

Tag pushes in Forgejo run `.forgejo/workflows/anothereditor-release.yml`, build unsigned Linux, Windows, and macOS release zips with the same Docker images used by the Skydas Qt6 packaging, prepare QScintilla6 for each target, publish them to the Forgejo release with a generated commit/artifact description, generate `SHA256SUMS` and `release-manifest.json`, deploy them using the same `EFNLAB_DEPLOY_*` variable names as the Skydas Qt6 packaging (`EFNLAB_DEPLOY_HOST=bal.efn.lt`, `EFNLAB_DEPLOY_PATH`, `EFNLAB_DEPLOY_PORT`, `EFNLAB_DEPLOY_PASS`), refresh the top-level `manifest.json` used by update checks, and send Telegram status notifications when those deployment variables are configured. Release container builds disable Go's automatic VCS stamping because version, commit, build date, and source line count are injected explicitly at compile time.

Use `Help > Check for Updates...` to compare the running version against the hardcoded release manifest at `https://buildhost.e1z0.net/AnotherEditor/manifest.json`. The update checker resolves artifact links relative to the manifest URL and reports available downloads with their recorded sizes and checksums.

## Current Features

- Tabbed QScintilla editor widgets
- New, Open, Save, Save As, Close Tab, and Exit
- Opening files from command-line arguments
- FTP/SFTP remote open, directory browsing, and save-back with cancellable status-bar transfer progress, optional Git credential helper integration, and global/per-remote credential-helper diagnostics
- Open Recent menu persisted between launches
- Undo, redo, cut, copy, paste, select all, find, find next, and replace
- Project directory indexing for identifier autocomplete
- Optional stdio LSP integration for installed language servers, with background manual/automatic completion refresh, hover lookups, diagnostics, go-to-definition, advertised capability checks, and a server status dialog
- Persisted view settings for font, word wrap, visible whitespace, tab width, indent width, tab indentation, and line endings
- Explicit editor light/dark palettes derived from the OS Qt theme, including lexer paper colors for readable dark-mode text
- Linux desktop entry, icon, and MIME integration assets
- macOS app bundle metadata for Finder Open With integration, optional Developer ID signing, and optional notarization
- Windows current-user Open With registry installer template plus Inno Setup installer packaging
- Manual release update check against deployed `release-manifest.json`
- Modified tab marker
- Close confirmation for modified tabs
- Status bar showing path, language, modified state, line endings, and cursor position
- UTF-8 editing, line numbers, auto indent, brace matching, current-line highlight, lexer keyword autocomplete, and project word autocomplete
- Lexer selection for C/C++, Markdown, Perl, PHP/HTML, Python, Batch, Bash, JSON, YAML, CSS, XML, JavaScript, Java, C#, and shell-like files
- Bundled TextMate-style grammar metadata for Go, Swift, and PowerShell keyword, comment, string, number, type, function, and operator highlighting/autocomplete, plus a Project menu action to load additional grammar directories

Go and Swift currently use bundled grammar metadata for language identification, regex-based scope highlighting, keyword highlighting, and keyword autocomplete because the current QScintilla bindings do not expose native Go or Swift lexers. PowerShell uses bundled grammar metadata instead of the Bash placeholder, but full PowerShell parser-level highlighting remains a later lexer/parser task.

Additional grammar directories can be loaded from `Project > Load Syntax Grammars...`. The current loader accepts JSON files with `name`, `scopeName`, `fileTypes`, `keywords`, `repository`, and a TextMate-style subset of `patterns` entries containing `include`, `match`, `begin`, `end`, nested `patterns`, `captures`, `beginCaptures`, `endCaptures`, and `name`. Includes support local repository references, same-scope repository references, `$self`, and `$base`. Pattern scopes are mapped to editor styles for comments, strings, numbers, types, functions, operators, and keywords.

Language servers can be started from `Project > Start Language Server...`. AnotherEditor launches the provided stdio command, opens the active local document, sends document close notifications when tabs close or change path, and can request `Project > Refresh LSP Completions`, enable `Project > Automatic LSP Completions`, use `Project > Show LSP Hover`, `Project > Refresh LSP Diagnostics`, `Project > Go to LSP Definition`, restart or stop the current server, or inspect `Project > Language Server Status`. LSP requests run in the background, cancel superseded tab operations, time out long-running requests, show active request counts and server `$/progress` notifications in the status bar, and apply results back on the Qt main thread when the originating tab is still current. Completion, hover, and definition actions are checked against the server capabilities advertised during initialization, while diagnostics remain notification-driven for compatibility with existing servers. Diagnostics are shown in the status bar, listed on demand, and marked with squiggle indicators. The integration is optional and depends on language servers such as `gopls`, `pylsp`, or `typescript-language-server` being installed by the user.

## License

GPL-3.0-only. QScintilla is GPL/commercial licensed, so this direct integration uses GPL-compatible licensing.
