Skip to content

Feat(vscode): Add the Table Diff view in the extension #4917

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 4 commits into from
Aug 15, 2025

Conversation

themisvaltinos
Copy link
Contributor

This adds support for table diff in the VSCode extension

Copy link
Contributor

@benfdking benfdking left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think there are a few things to fix.

Comment on lines 43 to 50
interface AllModelsResponse extends BaseResponse {
models: string[]
model_completions: ModelCompletion[]
keywords: string[]
macros: MacroCompletion[]
}

interface ModelCompletion {
name: string
description?: string
}

interface MacroCompletion {
name: string
description?: string
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The reason for this not being updated, is that it is legacy and so it really should just be dropped.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

removed these and created a new get_models_feature to use

Comment on lines +18 to +19
const panelType = (window as any).__SQLMESH_PANEL_TYPE__ || 'lineage'
return panelType === 'tablediff' ? <TableDiffPage /> : <LineagePage />
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I avoided this problem initially, but I am not sure this is quite the way we want to do it. To me it's about loading the specific page and we can break it up as much as possible for the performance.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I agree, check if what I added makes more sense

Comment on lines 67 to 166
const [selectedModel, setSelectedModel] = useState<string | undefined>(
undefined,
)
const { on } = useEventBus()
const queryClient = useQueryClient()

const {
data: models,
isLoading: isLoadingModels,
error: modelsError,
} = useApiModels()
const rpc = useRpc()

React.useEffect(() => {
const fetchFirstTimeModelIfNotSet = async (
models: Model[],
): Promise<string | undefined> => {
if (!Array.isArray(models)) {
return undefined
}
const activeFile = await rpc('get_active_file', {})
// @ts-ignore
if (!activeFile.fileUri) {
return models[0].name
}
// @ts-ignore
const fileUri: string = activeFile.fileUri
const filePath = URI.file(fileUri).path
const model = models.find(
(m: Model) => URI.file(m.full_path).path === filePath,
)
if (model) {
return model.name
}
return undefined
}
if (selectedModel === undefined && Array.isArray(models)) {
fetchFirstTimeModelIfNotSet(models).then(modelName => {
if (modelName && selectedModel === undefined) {
setSelectedModel(modelName)
} else {
setSelectedModel(models[0].name)
}
})
}
}, [models, selectedModel])

const modelsRecord =
Array.isArray(models) &&
models.reduce(
(acc, model) => {
acc[model.name] = model
return acc
},
{} as Record<string, Model>,
)

React.useEffect(() => {
const handleChangeFocusedFile = (fileUri: { fileUri: string }) => {
const full_path = URI.parse(fileUri.fileUri).path
const model = Object.values(modelsRecord).find(
m => URI.file(m.full_path).path === full_path,
)
if (model) {
setSelectedModel(model.name)
}
}

const handleSavedFile = () => {
queryClient.invalidateQueries()
}

const offChangeFocusedFile = on(
'changeFocusedFile',
handleChangeFocusedFile,
)
const offSavedFile = on('savedFile', handleSavedFile)

// If your event bus returns an "off" function, call it on cleanup
return () => {
if (offChangeFocusedFile) offChangeFocusedFile()
if (offSavedFile) offSavedFile()
}
}, [on, queryClient, modelsRecord])

if (modelsError) {
return <div>Error: {modelsError.message}</div>
}

if (
isLoadingModels ||
models === undefined ||
modelsRecord === false ||
selectedModel === undefined
) {
return <div>Loading models...</div>
}
if (!Array.isArray(models)) {
return <div>Error: Models data is not in the expected format</div>
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do you need any of this?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

excellent point removed all of it

}

return (
<div className="h-[100vh] w-[100vw]">
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Let's put this initial styling in the component.

}
result: {
ok: boolean
data?: any
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can't really have any

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yep revised all these anys

Comment on lines 190 to 214
for env in context.context.state_reader.get_environments():
environments[env.name] = EnvironmentInfo(
name=env.name,
snapshots=[s.fingerprint.to_identifier() for s in env.snapshots],
start_at=str(to_timestamp(env.start_at)),
plan_id=env.plan_id or "",
)

# Add prod if not present (mirroring web/server/api/endpoints/environments.py)
if c.PROD not in environments:
environments[c.PROD] = EnvironmentInfo(
name=c.PROD,
snapshots=[],
start_at=str(to_timestamp(c.EPOCH)),
plan_id="",
)

# Add default target environment if not present
if context.context.config.default_target_environment not in environments:
environments[context.context.config.default_target_environment] = EnvironmentInfo(
name=context.context.config.default_target_environment,
snapshots=[],
start_at=str(to_timestamp(c.EPOCH)),
plan_id="",
)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These are pretty big assumptions that aren't really listed in the "endpoint" definition.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

yeah mirroring the web version wasn't wise or needed, removed these

// No active editor, show a list of all models
const allModelsResult = await lspClient.call_custom_method(
'sqlmesh/all_models',
{ textDocument: { uri: '' } },
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I am not certain we want to be using the all_models endpoint which was a legacy endpoint used for autocomplete.


// Get the current active editor
const activeEditor = vscode.window.activeTextEditor
let selectedModelInfo: any = null
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

no any.

target: string
stats: Record<string, number>
sample: Record<string, any>
joined_sample: Record<string, any>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We shouldn't really haven anys

@themisvaltinos themisvaltinos force-pushed the themis/vscode_tablediff branch from 2267d46 to 8d93e96 Compare July 8, 2025 21:40
@themisvaltinos themisvaltinos force-pushed the themis/vscode_tablediff branch from e33f31a to e4d79de Compare August 14, 2025 13:07
<div
className="relative h-2 rounded overflow-hidden"
style={{
backgroundColor: 'var(--vscode-progressBar-background, #333)',
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why do we have backup colors here?

</div>
<div
className="relative h-2 rounded overflow-hidden"
style={{
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Use tailwind styling sinsteamd

Comment on lines 694 to 700
onMouseEnter={e =>
(e.currentTarget.style.color =
'var(--vscode-textLink-activeForeground)')
}
onMouseLeave={e =>
(e.currentTarget.style.color = themeColors.accent)
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unnecessary JS. Prefer CSS where you can do things in CSS
https://tailwindcss.com/docs/hover-focus-and-other-states#hover-focus-and-active

Comment on lines 632 to 636
style={{
color: themeColors.info,
backgroundColor: 'var(--vscode-input-background)',
border: `1px solid ${themeColors.border}`,
}}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Prefer tailwind over style.

</span>
</div>
))}
{Object.entries(schema_diff.modified).map(([col, type]) => (
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generally speaking turn a Row object into a component.

color: 'var(--vscode-editor-foreground)',
}}
>
{/* Header */}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If it's so big you need to break it apart with comments, just break it apart with a component.

@themisvaltinos themisvaltinos force-pushed the themis/vscode_tablediff branch 2 times, most recently from cbf8639 to 4699819 Compare August 15, 2025 09:22
@themisvaltinos themisvaltinos force-pushed the themis/vscode_tablediff branch from 4699819 to 5b6f318 Compare August 15, 2025 09:42
@themisvaltinos themisvaltinos enabled auto-merge (squash) August 15, 2025 09:42
@themisvaltinos themisvaltinos merged commit 0109a3a into main Aug 15, 2025
27 of 28 checks passed
@themisvaltinos themisvaltinos deleted the themis/vscode_tablediff branch August 15, 2025 09:57
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

2 participants