Skip to content
Merged
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
4 changes: 2 additions & 2 deletions .prettierrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,8 @@ module.exports = {
useTabs: false,
tabWidth: 2,
singleQuote: false,
trailingComma: "es5",
trailingComma: "all",
bracketSpacing: true,
jsxBracketSameLine: true,
arrowParens: "avoid",
arrowParens: "always",
}
6 changes: 3 additions & 3 deletions .vscode/extensions/squawk-dev/esbuild.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,11 @@ const esbuildProblemMatcherPlugin = {
build.onStart(() => {
console.log("[watch] build started")
})
build.onEnd(result => {
build.onEnd((result) => {
result.errors.forEach(({ text, location }) => {
console.error(`✘ [ERROR] ${text}`)
console.error(
` ${location.file}:${location.line}:${location.column}:`
` ${location.file}:${location.line}:${location.column}:`,
)
})
console.log("[watch] build finished")
Expand Down Expand Up @@ -50,7 +50,7 @@ async function main() {
}
}

main().catch(e => {
main().catch((e) => {
console.error(e)
process.exit(1)
})
16 changes: 8 additions & 8 deletions .vscode/extensions/squawk-dev/src/definitionProviders.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ export class TestSnapshotDefinitionProvider
public async provideDefinition(
document: vscode.TextDocument,
position: vscode.Position,
token: vscode.CancellationToken
token: vscode.CancellationToken,
): Promise<vscode.Definition | vscode.LocationLink[] | null> {
// crates/parser/src/snapshots/parser__alter_table_test__parse_alter_column.snap
const currentFilePath = document.uri.fsPath
Expand All @@ -25,7 +25,7 @@ export class TestSnapshotDefinitionProvider
const workspaceRoot = path.dirname(cratesDir)

const header = document.getText(
new Range(new Position(0, 0), new Position(3, 0))
new Range(new Position(0, 0), new Position(3, 0)),
)
let inputFilePathRel: string | null = null
for (const line of header.split("\n")) {
Expand All @@ -47,7 +47,7 @@ export class TestSnapshotDefinitionProvider

function getCursorPosition(
document: vscode.TextDocument,
position: Position
position: Position,
): [number | null, number | null] {
const cursorLine = document.lineAt(position.line).text

Expand Down Expand Up @@ -84,13 +84,13 @@ function getCursorPosition(
async function testFuncLocation(
testFilePath: vscode.Uri,
cursorPosition: Position,
document: vscode.TextDocument
document: vscode.TextDocument,
): Promise<vscode.Location | vscode.LocationLink[] | null> {
const testFileDoc = await vscode.workspace.openTextDocument(testFilePath)

const [byteOffsetStart, byteOffsetEnd] = getCursorPosition(
document,
cursorPosition
cursorPosition,
)

const destFunctionPositionStart = testFileDoc.positionAt(byteOffsetStart!)
Expand All @@ -116,7 +116,7 @@ async function testFuncLocation(

const originSelectionRange = new Range(
new Position(cursorPosition.line, leadingWhiteSpaceEnd),
new Position(cursorPosition.line, cursorLine.length)
new Position(cursorPosition.line, cursorLine.length),
)

return [
Expand All @@ -125,11 +125,11 @@ async function testFuncLocation(
targetUri: testFilePath,
targetRange: new Range(
destFunctionPositionStart,
destFunctionPositionEnd
destFunctionPositionEnd,
),
targetSelectionRange: new Range(
destFunctionPositionStart,
destFunctionPositionEnd
destFunctionPositionEnd,
),
} satisfies LocationLink,
]
Expand Down
16 changes: 8 additions & 8 deletions .vscode/extensions/squawk-dev/src/extension.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,15 @@ function computeSnapshotPath(sqlPath: string): string | undefined {
// crates/parser/src/snapshots/parser__test__alter_foreign_table.snap
return path.join(
sourceRoot,
`src/snapshots/parser__test__${testName}_${ok_or_err}.snap`
`src/snapshots/parser__test__${testName}_${ok_or_err}.snap`,
)
}

// Code lens provider to jump to snapshot files
class JumpToSnapshotCodeLensProvider implements vscode.CodeLensProvider {
public provideCodeLenses(
document: vscode.TextDocument,
token: vscode.CancellationToken
token: vscode.CancellationToken,
): vscode.CodeLens[] | Thenable<vscode.CodeLens[]> {
const position = new vscode.Position(0, 0)
const range = new vscode.Range(position, position)
Expand All @@ -52,8 +52,8 @@ export function activate(context: vscode.ExtensionContext) {
pattern: "**/snapshots/*.snap",
},
],
new TestSnapshotDefinitionProvider()
)
new TestSnapshotDefinitionProvider(),
),
)

context.subscriptions.push(
Expand All @@ -64,17 +64,17 @@ export function activate(context: vscode.ExtensionContext) {
// pattern: "**/test_data/**/*.sql",
// pattern: "*.sql",
},
new JumpToSnapshotCodeLensProvider()
)
new JumpToSnapshotCodeLensProvider(),
),
)

context.subscriptions.push(
vscode.commands.registerCommand(
"squawk-dev.jumpToSnapshot",
(path: string) => {
openSnapshotPath(path)
}
)
},
),
)
}

Expand Down
4 changes: 0 additions & 4 deletions PLAN.md
Original file line number Diff line number Diff line change
Expand Up @@ -35,7 +35,6 @@ support SQL embedded in other languages
- https://fljd.in/en/2024/11/25/substituting-a-variable-in-a-sql-script/

- https://www.timescale.com/blog/how-to-build-an-iot-pipeline-for-real-time-analytics-in-postgresql

- `$__timeFrom()`, `$__timeTo()`, and `$sensor_id`
- https://grafana.com/docs/grafana/latest/dashboards/variables/

Expand Down Expand Up @@ -1022,15 +1021,12 @@ size = 24 (0x18), align = 0x8, needs Drop
other fields?

- alignment

- https://r.ena.to/blog/optimizing-postgres-table-layout-for-maximum-efficiency/

- common values and distribution

- https://observablehq.com/documentation/cells/data-table#data-table-cell

- index size on hover

- https://www.peterbe.com/plog/index-size-postgresql

- data staleness -- if you have a daily batch job to calculate data, we could expose the staleness date
Expand Down
2 changes: 1 addition & 1 deletion crates/squawk_ide/src/completion.rs
Original file line number Diff line number Diff line change
Expand Up @@ -192,7 +192,7 @@ fn default_completions() -> Vec<CompletionItem> {
detail: None,
insert_text: Some(format!("{stmt} $0;")),
insert_text_format: Some(CompletionInsertTextFormat::Snippet),
trigger_completion_after_insert: false,
trigger_completion_after_insert: true,
})
.into_iter()
.collect()
Expand Down
47 changes: 47 additions & 0 deletions crates/squawk_wasm/src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -533,3 +533,50 @@ struct WasmSelectionRange {
end_line: u32,
end_column: u32,
}

#[wasm_bindgen]
pub fn completion(content: String, line: u32, col: u32) -> Result<JsValue, Error> {
let parse = squawk_syntax::SourceFile::parse(&content);
let line_index = LineIndex::new(&content);
let offset = position_to_offset(&line_index, line, col)?;
let items = squawk_ide::completion::completion(&parse.tree(), offset);

let converted: Vec<WasmCompletionItem> = items
.into_iter()
.map(|item| WasmCompletionItem {
label: item.label,
kind: match item.kind {
squawk_ide::completion::CompletionItemKind::Keyword => "keyword",
squawk_ide::completion::CompletionItemKind::Table => "table",
squawk_ide::completion::CompletionItemKind::Column => "column",
squawk_ide::completion::CompletionItemKind::Function => "function",
squawk_ide::completion::CompletionItemKind::Schema => "schema",
squawk_ide::completion::CompletionItemKind::Type => "type",
squawk_ide::completion::CompletionItemKind::Snippet => "snippet",
}
.to_string(),
detail: item.detail,
insert_text: item.insert_text,
insert_text_format: item.insert_text_format.map(|fmt| {
match fmt {
squawk_ide::completion::CompletionInsertTextFormat::PlainText => "plainText",
squawk_ide::completion::CompletionInsertTextFormat::Snippet => "snippet",
}
.to_string()
}),
trigger_completion_after_insert: item.trigger_completion_after_insert,
})
.collect();

serde_wasm_bindgen::to_value(&converted).map_err(into_error)
}

#[derive(Serialize)]
struct WasmCompletionItem {
label: String,
kind: String,
detail: Option<String>,
insert_text: Option<String>,
insert_text_format: Option<String>,
trigger_completion_after_insert: bool,
}
10 changes: 5 additions & 5 deletions js/install.js
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ function getCachedPath(url) {
return path.join(
getNpmCache(),
"squawk-cli",
`${digest}-${path.basename(url).replace(/[^a-zA-Z0-9.]+/g, "-")}`
`${digest}-${path.basename(url).replace(/[^a-zA-Z0-9.]+/g, "-")}`,
)
}

Expand Down Expand Up @@ -134,7 +134,7 @@ function downloadBinary() {
"accept-encoding": "gzip, deflate, br",
},
redirect: "follow",
}).then(response => {
}).then((response) => {
if (!response.ok) {
throw new Error(`Received ${response.status}: ${response.statusText}`)
}
Expand All @@ -147,10 +147,10 @@ function downloadBinary() {

return new Promise((resolve, reject) => {
response.body
.on("error", e => reject(e))
.on("error", (e) => reject(e))
.pipe(decompressor)
.pipe(fs.createWriteStream(tempPath, { mode: 0o755 }))
.on("error", e => reject(e))
.on("error", (e) => reject(e))
.on("close", () => resolve())
}).then(() => {
fs.copyFileSync(tempPath, cachedPath)
Expand All @@ -162,7 +162,7 @@ function downloadBinary() {

downloadBinary()
.then(() => process.exit(0))
.catch(e => {
.catch((e) => {
console.error(e)
process.exit(1)
})
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@
"@typescript-eslint/parser": "^3.3.0",
"eslint": "^7.2.0",
"eslint-plugin-import": "^2.21.2",
"prettier": "^2.0.5",
"prettier": "^3.8.0",
"typescript": "^3.9.5"
},
"dependencies": {
Expand Down
10 changes: 10 additions & 0 deletions playground/src/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
provideReferences,
provideDocumentSymbols,
provideSelectionRanges,
provideCompletionItems,
} from "./providers"

const modes = ["Lint", "Syntax Tree", "Tokens"] as const
Expand Down Expand Up @@ -417,6 +418,14 @@ function registerMonacoProviders() {
provideSelectionRanges,
})

const completionProvider = monaco.languages.registerCompletionItemProvider(
"pgsql",
{
triggerCharacters: ["."],
provideCompletionItems,
},
)

return () => {
languageConfig.dispose()
codeActionProvider.dispose()
Expand All @@ -426,6 +435,7 @@ function registerMonacoProviders() {
documentSymbolProvider.dispose()
inlayHintsProvider.dispose()
selectionRangeProvider.dispose()
completionProvider.dispose()
tokenProvider.dispose()
}
}
Expand Down
63 changes: 63 additions & 0 deletions playground/src/providers.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as monaco from "monaco-editor"
import {
code_actions,
completion,
document_symbols,
find_references,
goto_definition,
Expand Down Expand Up @@ -243,3 +244,65 @@ export async function provideSelectionRanges(
return []
}
}

function convertCompletionKind(
kind: string,
): monaco.languages.CompletionItemKind {
switch (kind) {
case "keyword":
return monaco.languages.CompletionItemKind.Keyword
case "table":
return monaco.languages.CompletionItemKind.Class
case "column":
return monaco.languages.CompletionItemKind.Field
case "function":
return monaco.languages.CompletionItemKind.Function
case "schema":
return monaco.languages.CompletionItemKind.Module
case "type":
return monaco.languages.CompletionItemKind.TypeParameter
case "snippet":
return monaco.languages.CompletionItemKind.Snippet
default:
return monaco.languages.CompletionItemKind.Text
}
}

export async function provideCompletionItems(
model: monaco.editor.ITextModel,
position: monaco.Position,
): Promise<monaco.languages.CompletionList> {
const content = model.getValue()
if (!content) return { suggestions: [] }

try {
const items = completion(
content,
position.lineNumber - 1,
position.column - 1,
)

const suggestions: monaco.languages.CompletionItem[] = items.map(
(item) => ({
label: item.label,
kind: convertCompletionKind(item.kind),
detail: item.detail ?? undefined,
insertText: item.insert_text ?? item.label,
insertTextRules:
item.insert_text_format === "snippet"
? monaco.languages.CompletionItemInsertTextRule.InsertAsSnippet
: undefined,
command: item.trigger_completion_after_insert
? { id: "editor.action.triggerSuggest", title: "Trigger Suggest" }
: undefined,
sortText: item.kind === "schema" ? `z${item.label}` : item.label,
range: undefined as unknown as monaco.IRange,
}),
)

return { suggestions }
} catch (e) {
console.error("Error in provideCompletionItems:", e)
return { suggestions: [] }
}
}
Loading
Loading