Skip to content

Commit

Permalink
Merge branch 'master' of https://github.com/DanielGavin/ols
Browse files Browse the repository at this point in the history
  • Loading branch information
DanielGavin committed Jan 11, 2025
2 parents 88a91d1 + eec5781 commit d22499b
Show file tree
Hide file tree
Showing 14 changed files with 142 additions and 51 deletions.
3 changes: 2 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -15,4 +15,5 @@
/odinfmt
tests/tests
ols.json
.vscode/
.vscode/
deno.lock
53 changes: 47 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -228,24 +228,65 @@ require'lspconfig'.ols.setup {

### Emacs

For Emacs, there are two packages available for LSP; lsp-mode and eglot.

The latter is built-in, spec-compliant and favours built-in Emacs functionality and the former offers richer UI elements and automatic installation for some of the servers.

In either case, you'll also need an associated major mode.

Pick either of the below, the former is likely to be more stable but the latter will allow you to take advantage of tree-sitter and other packages that integrate with it.

The `use-package` statements below assume you're using a package manager like Straight or Elpaca and as such should be taken as references rather than guaranteed copy/pasteable. If you're using `package.el` or another package manager then you'll have to look into instructions for that yourself.

```elisp
;; Enable odin-mode and configure OLS as the language server
(use-package! odin-mode
:mode ("\\.odin\\'" . odin-mode)
:hook (odin-mode . lsp))
(use-package odin-mode
:ensure (:host github :repo "mattt-b/odin-mode")
:mode ("\\.odin\\'" . odin-mode))
;; Or use the WIP tree-sitter mode
(use-package odin-ts-mode
:ensure (:host github :repo "Sampie159/odin-ts-mode")
:mode ("\\.odin\\'" . odin-ts-mode))
```

And then choose either the built-in `eglot` or `lsp-mode` packages below. Both should work very similarly.

#### lsp-mode

```elisp
;; Pull the lsp-mode package
(use-package lsp-mode
:commands (lsp lsp-deferred))
;; Set up OLS as the language server for Odin, ensuring lsp-mode is loaded first
(with-eval-after-load 'lsp-mode
(setq-default lsp-auto-guess-root t) ;; Helps find the ols.json file with Projectile or project.el
(setq lsp-language-id-configuration (cons '(odin-mode . "odin") lsp-language-id-configuration))
(add-to-list 'lsp-language-id-configuration '(odin-mode . "odin"))
(add-to-list 'lsp-language-id-configuration '(odin-ts-mode . "odin"))
(lsp-register-client
(make-lsp-client :new-connection (lsp-stdio-connection "/path/to/ols/executable") ;; Adjust the path here
:major-modes '(odin-mode)
:major-modes '(odin-mode odin-ts-mode)
:server-id 'ols
:multi-root t))) ;; Ensures lsp-mode sends "workspaceFolders" to the server
(add-hook 'odin-mode-hook #'lsp)
;; Add a hook to autostart OLS
(add-hook 'odin-mode-hook #'lsp-deferred)
(add-hook 'odin-ts-mode-hook #'lsp-deferred) ;; If you're using the TS mode
```

#### eglot

```elisp
;; Add OLS to the list of available programs
;; NOTE: As of Emacs 30, this is not needed.
(with-eval-after-load 'eglot
(add-to-list 'eglot-server-programs '((odin-mode odin-ts-mode) . ("ols"))))
;; Add a hook to autostart OLS
(add-hook 'odin-mode-hook #'eglot-ensure)
(add-hook 'odin-ts-mode-hook #'eglot-ensure) ;; If you're using the TS mode
```

### Helix
Expand Down
3 changes: 3 additions & 0 deletions build.sh
Original file line number Diff line number Diff line change
Expand Up @@ -46,5 +46,8 @@ then
exit 0
fi

version="$(git describe --tags --abbrev=7)"
version="${version%-*}:${version##*-}"
sed "s|VERSION :: .*|VERSION :: \"${version}\"|g" src/main.odin > /tmp/main.odin.build && mv -f /tmp/main.odin.build src/main.odin

odin build src/ -show-timings -collection:src=src -out:ols -microarch:native -no-bounds-check -o:speed $@
12 changes: 7 additions & 5 deletions editors/vscode/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -14,9 +14,6 @@
"categories": [
"Programming Languages"
],
"activationEvents": [
"onLanguage:odin"
],
"icon": "images/emblem.png",
"main": "./out/extension.js",
"contributes": {
Expand All @@ -37,8 +34,13 @@
"category": "Odin Language Server"
},
{
"command": "ols.createOls",
"title": "Create ols.json file in project",
"command": "ols.editProjectConfig",
"title": "Edit per-project config file (ols.json), creating it if necessary",
"category": "Odin Language Server"
},
{
"command": "ols.editUserConfig",
"title": "Edit per-user config file (ols.json), creating it if necessary",
"category": "Odin Language Server"
}
],
Expand Down
107 changes: 70 additions & 37 deletions editors/vscode/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@
import * as vscode from 'vscode';
import * as path from "path";
import * as os from "os";
import { promises as fs, PathLike, constants, writeFileSync } from "fs";
import { promises as fs, constants, writeFileSync} from "fs";

var AdmZip = require('adm-zip');

Expand All @@ -20,13 +20,24 @@ import { RunnableCodeLensProvider } from "./run";
import { PersistentState } from './persistent_state';
import { Config } from './config';
import { fetchRelease, download } from './net';
import { getPathForExecutable, isOdinInstalled } from './toolchain';
import { isOdinInstalled } from './toolchain';
import { Ctx } from './ctx';
import { runDebugTest, runTest } from './commands';
import { watchOlsConfigFile } from './watch';

const onDidChange: vscode.EventEmitter<void> = new vscode.EventEmitter<void>();

const defaultConfig = JSON.stringify(
{
$schema: "https://raw.githubusercontent.com/DanielGavin/ols/master/misc/ols.schema.json",
enable_document_symbols: true,
enable_hover: true,
enable_snippets: true
},
null,
4,
);

let ctx: Ctx;

export async function activate(context: vscode.ExtensionContext) {
Expand Down Expand Up @@ -102,32 +113,40 @@ export async function activate(context: vscode.ExtensionContext) {
ctx.registerCommand("runDebugTest", runDebugTest);
ctx.registerCommand("runTest", runTest);

const olsFile = path.join(workspaceFolder.uri.fsPath, "ols.json");

fs.access(olsFile, constants.F_OK).catch(async err => {
if (err) {
const projectConfigPath = path.join(workspaceFolder.uri.fsPath, "ols.json");
const userConfigPath = path.join(path.dirname(serverPath), "ols.json");

fs.access(projectConfigPath, constants.F_OK).catch(async (_e1) => {
fs.access(userConfigPath, constants.F_OK).catch( async (_e2) => {
if (!config.askCreateOLS) {
return;
}

const userResponse = await vscode.window.showInformationMessage(
"No ols config file in the workspace root folder. Do you wish to create one?",
"No ols config file found. Do you wish to create one?",
"Yes",
"No",
"Don't ask again"
"Don't ask again",
);

if (userResponse === "Yes") {
createOlsConfig(ctx);
const clarification = await vscode.window.showInformationMessage(
"should it be specific to this project or to all your odin projects?",
"This project",
"All projects",
);
if (clarification == "This project") {
createOrEditProjectConfig();
parseOlsFile(config, projectConfigPath);
} else {
createOrEditUserConfig(serverPath);
parseOlsFile(config, userConfigPath);
}
} else if (userResponse === "Don't ask again") {
config.updateAskCreateOLS(false);
return;
}

}

parseOlsFile(config, olsFile);
})
});

if(!isOdinInstalled()) {
Expand All @@ -147,14 +166,18 @@ export async function activate(context: vscode.ExtensionContext) {
client.start();
});

vscode.commands.registerCommand("ols.createOls", async() => {
createOlsConfig(ctx);
vscode.commands.registerCommand("ols.editProjectConfig", async() => {
createOrEditProjectConfig();
});

vscode.commands.registerCommand("ols.editUserConfig", async () => {
createOrEditUserConfig(serverPath);
});

client.start();

parseOlsFile(config, olsFile);
watchOlsConfigFile(ctx, olsFile);
parseOlsFile(config, projectConfigPath);
watchOlsConfigFile(ctx, projectConfigPath);
}

async function bootstrap(config: Config, state: PersistentState): Promise<string> {
Expand Down Expand Up @@ -219,35 +242,45 @@ async function removeOldServers(config: Config, state: PersistentState): Promise
}
}

export function createOlsConfig(ctx: Ctx) {
const odinPath = getPathForExecutable("odin");

const corePath = path.resolve(path.join(path.dirname(odinPath), "core"));

const config = {
$schema: "https://raw.githubusercontent.com/DanielGavin/ols/master/misc/ols.schema.json",
enable_document_symbols: true,
enable_hover: true,
enable_snippets: true
};

const olsPath = vscode.workspace.workspaceFolders![0].uri.fsPath;
export function createOrEditProjectConfig() {
const projectConfigPath = vscode.workspace.workspaceFolders![0].uri.fsPath;
openFileAndCreateIfNotExists("ols.json", projectConfigPath, defaultConfig);
}

const edit = new vscode.WorkspaceEdit();
export function createOrEditUserConfig(serverPath: string) {
const userConfigPath = path.dirname(serverPath);
openFileAndCreateIfNotExists("ols.json", userConfigPath, defaultConfig);
}

const content = JSON.stringify(config, null, 4);
function openFileAndCreateIfNotExists(file: string, folder: string, defaultContents: string) {
const filePath = path.join(folder, file);
console.log(filePath);

writeFileSync(path.join(olsPath, "ols.json"), content);
vscode.workspace.openTextDocument(filePath).then(
(document) => { vscode.window.showTextDocument(document) },
() => {
writeFileSync(filePath, defaultContents);
vscode.workspace.openTextDocument(filePath).then(
(document) => { vscode.window.showTextDocument(document) }
);
}
);

}

export async function parseOlsFile(config: Config, file: string) {
/*
We have to parse the collections that they have specificed through the json(This will be changed when odin gets it's own builder files)
*/
fs.readFile(file).then((data) => {
const conf = JSON.parse(data.toString());
config.collections = conf.collections;
});
fs.readFile(file).then(
(data) => {
const conf = JSON.parse(data.toString());
config.collections = conf.collections;
},
(error) => {
console.info("no ols.json found in workspace");
},
);
}

function serverPath(config: Config): string | null {
Expand Down
2 changes: 1 addition & 1 deletion editors/vscode/src/toolchain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ function lookupInPath(exec: string): string | undefined {
return pathToOdin;
}
} catch (realpathError) {
console.error("realpathError:", realpathError)
console.debug("couldn't find odin at", candidates[i], "on account of", realpathError)
}
}

Expand Down
2 changes: 1 addition & 1 deletion misc/ols.schema.json
Original file line number Diff line number Diff line change
Expand Up @@ -106,7 +106,7 @@
},
"exclude_path": {
"type": "array",
"description": "List of paths that will be exldued from workspace symbol searches.",
"description": "List of paths that will be excluded from workspace symbol searches.",
"items": {
"type": "string"
}
Expand Down
1 change: 1 addition & 0 deletions src/common/ast.odin
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#+feature dynamic-literals
package common

import "core:fmt"
Expand Down
5 changes: 5 additions & 0 deletions src/main.odin
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,7 @@ import "core:sys/windows"
import "src:common"
import "src:server"

VERSION :: "dev-2024-11-9:g584f01b"

os_read :: proc(handle: rawptr, data: []byte) -> (int, int) {
ptr := cast(^os.Handle)handle
Expand Down Expand Up @@ -101,6 +102,10 @@ end :: proc() {
}

main :: proc() {
if len(os.args) > 1 && os.args[1] == "version" {
fmt.println("ols version", VERSION)
os.exit(0)
}
reader := server.make_reader(os_read, cast(rawptr)&os.stdin)
writer := server.make_writer(os_write, cast(rawptr)&os.stdout)

Expand Down
1 change: 1 addition & 0 deletions src/server/build.odin
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#+feature dynamic-literals
package server

import "base:runtime"
Expand Down
1 change: 1 addition & 0 deletions src/server/completion.odin
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#+feature dynamic-literals
package server

import "core:fmt"
Expand Down
1 change: 1 addition & 0 deletions src/server/hover.odin
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#+feature dynamic-literals
package server

import "core:fmt"
Expand Down
1 change: 1 addition & 0 deletions src/server/requests.odin
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#+feature dynamic-literals
package server

import "base:intrinsics"
Expand Down
1 change: 1 addition & 0 deletions src/server/snippets.odin
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
#+feature dynamic-literals
package server

Snippet_Info :: struct {
Expand Down

0 comments on commit d22499b

Please sign in to comment.