commit 2f953bf0495054ce6b4d73c3791f271d8079f907
Author: Lou Woell <lou.woell@posteo.de>
Date:   Wed,  3 Sep 2025 23:53:51 +0200

Initial Commit

Diffstat:
AMakefile | 31+++++++++++++++++++++++++++++++
Acmd/harehelper/find.ha | 100+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmd/harehelper/helper.ha | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Acmd/harehelper/list_modules.ha | 41+++++++++++++++++++++++++++++++++++++++++
Acmd/harehelper/list_symbols.ha | 77+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Aharedoc.el | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
6 files changed, 401 insertions(+), 0 deletions(-)

diff --git a/Makefile b/Makefile @@ -0,0 +1,31 @@ +.POSIX: +.SUFFIXES: +HARE=hare +HAREFLAGS= + +DESTDIR= +PREFIX=$(HOME)/.local +BINDIR=$(PREFIX)/bin + +HARE_SOURCES != find . -name '*.ha' + +BINS = harehelper + +all: $(BINS) + +$(BINS): $(HARE_SOURCES) + $(HARE) build $(HAREFLAGS) -o $@ cmd/$@/ + +check: + $(HARE) test $(HAREFLAGS) + +clean: + rm -f $BINS + +install: + install -Dm755 example-cmd $(DESTDIR)$(BINDIR)/example-cmd + +uninstall: + rm -f $(DESTDIR)$(BINDIR)/example-cmd + +.PHONY: all check clean install uninstall diff --git a/cmd/harehelper/find.ha b/cmd/harehelper/find.ha @@ -0,0 +1,100 @@ +use bufio; +use fmt; +use fs; +use getopt; +use hare::ast; +use hare::lex; +use hare::parse; +use hare::unparse; +use io; +use os; + +fn find(cmd: getopt::command) void = { + + let id = parse::identstr(cmd.args[0])!; + let file = cmd.args[1]; + + match(find_uid(id, file)) { + case let id_exp: ast::ident => + unparse::ident( + os::stdout, + id_exp, + )!; + fmt::println()!; + case => void; + }; +}; + +fn list_imports (path: str) []ast::import = { + const input = match (os::open(path)) { + case let f: io::file => + yield f; + case let err: fs::error => + fmt::fatalf("Error reading {}: {}", path, fs::strerror(err)); + }; + defer io::close(input)!; + let sc = bufio::newscanner(input); + defer bufio::finish(&sc); + let lexer = lex::init(&sc, path, lex::flag::NONE); + + return parse::imports(&lexer)!; +}; + +fn find_uid (id: ast::ident, path: str) (void | ast::ident) = { + + let imports = list_imports(path); + defer ast::imports_finish(imports); + + for (let i .. imports) { + + let ident = i.ident; + let check = false; + let ns = ident_ns(id); + let interned = false; + + match(i.bindings){ + case let a: ast::import_alias => + ident = [a]; + case let members: ast::import_members => + if(len(id) > 1) continue; + for (let s .. members) { + check = (s == id[0]); + interned = true; + break; + }; + case ast::import_wildcard => + // todo check all symbols; + void; + case void => void; + }; + + if(!check){ + check = ast::ident_eq(ns, ident) || + ast::ident_eq(ns, ident_last(ident)); + }; + + if (check) { + let id_exp = ast::ident_dup(i.ident); + if(len(id) > 1 && !interned){ + let end = ident_last(id); + let id_end = ast::ident_dup(end); + append(id_exp, id_end[0])!; + }; + return id_exp; + }; + }; + + return void; +}; + +fn ident_last(id: ast::ident) ast::ident = { + let l = len(id); + + if(l > 1) return id[l - 1 ..] else return id; +}; + +fn ident_ns(id: ast::ident) ast::ident = { + let l = len(id); + + if(l > 1) return id[..l - 1] else return id; +}; diff --git a/cmd/harehelper/helper.ha b/cmd/harehelper/helper.ha @@ -0,0 +1,67 @@ +use bufio; +use dirs; +use fmt; +use getopt; +use hare::module; +use io; +use os; +use path; + +def HAREPATH = "/usr/src/hare/stdlib/:/usr/src/hare/third-party:."; + +const help: []getopt::help = [ + "help with hare", + ('h', "show help"), + ("list", [ + "list symbols in package", + "identier" + ]: []getopt::help ), + ("find", [ + "find module for identifier", + "args", + ]: []getopt::help), + ("list-modules", [ + "findc module for identifier", + ]: []getopt::help), +]; + +export fn main () void = { + + const ctx = module::context { + harepath = os::tryenv("HAREPATH", HAREPATH), + harecache = match (os::getenv("HARECACHE")) { + case let s: str => + yield s; + case void => + yield dirs::cache("hare"); + }, + tags = [], + }; + + const cmd = getopt::parse(os::args, help...); + defer getopt::finish(&cmd); + + match(cmd.subcmd){ + case void => void; + case let c: (str, *getopt::command) => + switch(c.0) { + case "find" => + find(*c.1); + case "list" => + list(*c.1, &ctx); + case "list-modules" => + list_modules(&ctx); + case => void; + }; + os::exit(0); + }; + + for (let (k, v) .. cmd.opts) { + switch(k){ + case 'h' => + getopt::printhelp(os::stdout, "cleandir", help)!; + os::exit(0); + case => void; + }; + }; +}; diff --git a/cmd/harehelper/list_modules.ha b/cmd/harehelper/list_modules.ha @@ -0,0 +1,41 @@ +use fmt; +use hare::module; +use hare::parse; +use hare::unparse; +use os; +use path; +use strings; + +fn get_modules (ctx: *module::context) []module::module = { + + let res: []module::module = []; + + const harepath = strings::split(ctx.harepath, ":")!; + let mod = path::init()!; + + for(let p .. harepath) { + path::set(&mod, p)!; + let r = module::gather_submodules(ctx, &res, &mod, true); + + match (r) { + case let e: hare::module::error => + fmt::println(module::strerror(e))!; + case => void; + }; + }; + + return res; +}; + +fn list_modules (ctx: *module::context) void = { + const mods = get_modules(ctx); + for (let m &.. mods) { + unparse::ident( + os::stdout, + m.ns, + )!; + fmt::println()!; + module::finish(m); + }; + free(mods); +}; diff --git a/cmd/harehelper/list_symbols.ha b/cmd/harehelper/list_symbols.ha @@ -0,0 +1,77 @@ +use bufio; +use fs; +use hare::lex; +use fmt; +use getopt; +use hare::ast; +use hare::module; +use hare::parse; +use hare::unparse; +use io; +use os; + +fn list (cmd: getopt::command, ctx: *module::context) void = { + + let id = parse::identstr(cmd.args[0])!; + defer ast::ident_free(id); + + let list = list_symbols(ctx, id); + for(let i .. list) { + unparse::ident( + os::stdout, + i, + )!; + fmt::println()!; + ast::ident_free(i); + }; + free(list); +}; + +fn list_symbols (ctx: *module::context, id: ast::ident) []ast::ident = { + + let (path, src) = module::find(ctx, id)!; + + let res: []ast::ident = []; + + for(let s .. src.ha) { + let decls = scan(s)!; + for (let d .. decls) { + if(!d.exported) continue; + match(d.decl){ + case let d: []ast::decl_const => void; + for (let t .. d) { + append(res, ast::ident_dup(t.ident))!; + }; + case let d: []ast::decl_global => void; + for (let t .. d) { + append(res, ast::ident_dup(t.ident))!; + }; + case let d: []ast::decl_type => void; + for (let t .. d) { + append(res, ast::ident_dup(t.ident))!; + }; + case let d: ast::decl_func => void; + append(res, ast::ident_dup(d.ident))!; + case let d: ast::assert_expr => void; + }; + }; + }; + + return res; +}; + +fn scan(path: str) ([]ast::decl | parse::error) = { + const input = match (os::open(path)) { + case let f: io::file => + yield f; + case let err: fs::error => + fmt::fatalf("Error reading {}: {}", path, fs::strerror(err)); + }; + defer io::close(input)!; + let sc = bufio::newscanner(input); + defer bufio::finish(&sc); + let lexer = lex::init(&sc, path, lex::flag::NONE); + let su = parse::subunit(&lexer)?; + ast::imports_finish(su.imports); + return su.decls; +}; diff --git a/haredoc.el b/haredoc.el @@ -0,0 +1,85 @@ +;;; haredoc.el --- Haredoc Integratio in Emacs -*- lexical-binding: t -*- + +;; Author: Lou Woell +;; Version: 0.1 +;; Package-Requires: +;; Homepage: +;; Keywords: + +;; This file is not part of GNU Emacs + +;; This program is free software: you can redistribute it and/or modify +;; it under the terms of the GNU General Public License as published by +;; the Free Software Foundation, either version 3 of the License, or +;; (at your option) any later version. + +;; This program is distributed in the hope that it will be useful, +;; but WITHOUT ANY WARRANTY; without even the implied warranty of +;; MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +;; GNU General Public License for more detai + +;; You should have received a copy of the GNU General Public License +;; along with this program. If not, see <https://www.gnu.org/licenses/>. + +;;; Commentary: + +;; Better emacs integration for hare. + +;;; TODO: + +;; - make references in docs clickable links + +;;; Code: + +(require 'ansi-color) + +(defvar hare/helperbindir (f-dirname (macroexp-file-name))) +(defvar hare/helperbin (concat hare/helperbindir "/harehelper")) + +(defun hh/exec (strings) + (shell-command-to-string (string-join strings " "))) + +(defun hare/get-modules () + "Return list of Hare modules." + (string-split (hh/exec (list hare/helperbin "list-modules")) "\n")) + +(defun hare/read-module (&optional init) + (completing-read "Pick Module: " (hare/get-modules) nil nil init)) + +(defun hare/identifier-at-point () + (let ((id (cond + ((use-region-p) ;; Use region or the current word + (buffer-substring-no-properties (region-beginning) + (region-end))) + (t (progn (thing-at-point-looking-at + "\\(\\(\\sw\\|\\s_\\)+::\\)*\\(\\sw\\|\\s_\\)+") + (string-replace "*" "" (match-string 0))))))) + (hh/exec (list hare/helperbin "find" id (buffer-file-name))))) + +;; Shows haredoc for the selected or word. +(defun haredoc (&optional sign) + "Run haredoc with the current word under the cursor and display +the results in a new buffer." + (interactive) + (let* ((current-word + (cond + (sign sign) + (t (hare/read-module)))) + + (command (concat "haredoc -a -F tty " current-word)) + (output (shell-command-to-string command)) + (buffer-name "*haredoc*")) + (with-output-to-temp-buffer buffer-name + (with-current-buffer buffer-name + (insert output) + (ansi-color-apply-on-region (point-min) (point-max)) + (haredoc-mode))))) + +(defun haredoc-describe-thing-at-point () + (interactive) + (haredoc (hare/identifier-at-point))) + +(define-derived-mode haredoc-mode help-mode "🐇") + +(provide 'haredoc) +;;; haredoc.el ends here