Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 2 additions & 10 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

76 changes: 67 additions & 9 deletions src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,19 +4,68 @@ import { log, outputChannel, vlsOutputChannel } from "logger"
import vscode, { ConfigurationChangeEvent, ExtensionContext, workspace } from "vscode"
import { LanguageClient, LanguageClientOptions, ServerOptions } from "vscode-languageclient/node"
import { installV, isVInstalled } from "./utils"
import { VDocumentFormattingProvider } from "./formatProvider"

export let client: LanguageClient | undefined

// The verified dynamic linker path for Debian 64-bit systems
const linkerPath = "/lib64/ld-linux-x86-64.so.2"
const isLinux = process.platform === "linux"
// The absolute path to your V installation root
const vRootPath = "/home/tshimo/v"
// The required arguments for VLS to use the standard I/O communication method
const vlsArgs = ["--stdio"]

async function createAndStartClient(): Promise<void> {
// getVls() will resolve VLS path now that it's in ~/.local/bin/
const vlsPath = await getVls()

const serverOptions: ServerOptions = {
run: { command: vlsPath },
debug: { command: vlsPath },
// CRITICAL FIX: Define Environment variables for VLS execution
const vlsEnvironment = {
// 1. Tell VLS where the V installation is located
V_HOME: vRootPath,
// 2. Ensure the V executable is discoverable in the PATH
PATH: `${vRootPath}:${process.env.PATH || ""}`,
// Preserve existing environment variables
...process.env,
}

let serverOptions: ServerOptions

if (isLinux) {
// FIX: Use the linker path to execute the VLS binary
serverOptions = {
run: {
command: linkerPath,
// Pass the VLS path first, then its arguments
args: [vlsPath, ...vlsArgs],
options: {
env: vlsEnvironment, // Inject environment variables
cwd: vRootPath, // Set working directory
},
},
debug: {
command: linkerPath,
args: [vlsPath, ...vlsArgs],
options: {
env: vlsEnvironment,
cwd: vRootPath,
},
},
}
} else {
// Original logic for non-Linux systems
serverOptions = {
run: { command: vlsPath, args: vlsArgs },
debug: { command: vlsPath, args: vlsArgs },
}
}

const clientOptions: LanguageClientOptions = {
documentSelector: [{ scheme: "file", language: "v" }],
initializationOptions: {
enableHover: true,
},
outputChannel: vlsOutputChannel,
synchronize: {
fileEvents: vscode.workspace.createFileSystemWatcher("**/*.v"),
Expand All @@ -30,14 +79,14 @@ async function createAndStartClient(): Promise<void> {
}

export async function activate(context: ExtensionContext): Promise<void> {
// Register output channels so users can open them even without VLS.
// Register output channels
context.subscriptions.push(outputChannel, vlsOutputChannel)

// Check for V only if it's not installed
// Original V installation check logic
if (!(await isVInstalled())) {
const selection = await vscode.window.showInformationMessage(
"The V programming language is not detected on this system. Would you like to install it?",
{ modal: true }, // Modal makes the user have to choose before continuing
{ modal: true },
"Yes",
"No",
)
Expand All @@ -50,13 +99,22 @@ export async function activate(context: ExtensionContext): Promise<void> {
// Register commands regardless of whether VLS is enabled
await registerCommands(context)

// --- Register Document Formatter ---
const formattingProvider = new VDocumentFormattingProvider()
context.subscriptions.push(
vscode.languages.registerDocumentFormattingEditProvider(
{ scheme: "file", language: "v" },
formattingProvider,
),
)
// --- END Register Document Formatter ---

// Only start the language server if the user enabled it in settings.
if (isVlsEnabled()) {
try {
await createAndStartClient()
} catch (err) {
// If starting the client fails, log and continue. Users can still
// use non-LSP features of the extension.
// If starting the client fails, log and continue.
console.error("Failed to start VLS:", err)
vscode.window.showErrorMessage("Failed to start VLS. See output for details.")
outputChannel.show()
Expand All @@ -67,7 +125,7 @@ export async function activate(context: ExtensionContext): Promise<void> {

registerVlsCommands(context, client)

// React to configuration changes: enable/disable or request restart.
// Original configuration change handler logic
workspace.onDidChangeConfiguration(async (e: ConfigurationChangeEvent) => {
const vlsEnabled = isVlsEnabled()

Expand Down
63 changes: 63 additions & 0 deletions src/formatProvider.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import * as vscode from "vscode"
import { execSync, ExecSyncOptionsWithStringEncoding } from "child_process"
//import * as path from "path"

export class VDocumentFormattingProvider implements vscode.DocumentFormattingEditProvider {
public provideDocumentFormattingEdits(
document: vscode.TextDocument,
): vscode.ProviderResult<vscode.TextEdit[]> {
// Use the absolute, known-working path for the V compiler
const vExecutablePath = "/home/tshimo/v/v" // <--- ABSOLUTE PATH

// Command: v fmt -
const commandToRun = `"${vExecutablePath}" fmt -`

const execOptions: ExecSyncOptionsWithStringEncoding = {
input: document.getText(),
encoding: "utf8",
timeout: 5000,
// Set CWD to V's source directory to help it find internal tools (Vfmt)
cwd: "/home/tshimo/v",
}

try {
console.log(`[V Formatter] Running command: ${commandToRun}`)

const buffer = execSync(commandToRun, execOptions)

const fullRange = new vscode.Range(
document.lineAt(0).range.start,
document.lineAt(document.lineCount - 1).range.end,
)

// Return the formatted text to VS Code
return [vscode.TextEdit.replace(fullRange, buffer)]
} catch (error) {
if (error instanceof Error) {
const childProcessError = error as {
code?: string
stderr?: Buffer | string
path?: string
}

let errorMessage = "V Formatter failed: Check Debug Console."

if (childProcessError.stderr) {
const stderrOutput = childProcessError.stderr.toString()
console.error("[V Formatter] V Execution Error (stderr):", stderrOutput)
errorMessage = `V Formatter failed. Output: ${stderrOutput.substring(0, 80)}...`
} else if (childProcessError.code) {
console.error(
"[V Formatter] Error Code/Path:",
childProcessError.code,
childProcessError.path,
)
errorMessage = `V Formatter failed. Code: ${childProcessError.code}.`
}

vscode.window.showErrorMessage(errorMessage)
}
return []
}
}
}
Loading