From dc5d899930d3ec4c8dcc1ac30d134dec12e261ea Mon Sep 17 00:00:00 2001 From: krassowski Date: Sun, 5 Sep 2021 00:33:23 +0100 Subject: [PATCH] Improve translations in diagnostics, update translation domain --- packages/completion-theme/src/index.ts | 2 +- .../src/components/utils.spec.ts | 34 ++- .../jupyterlab-lsp/src/components/utils.tsx | 11 +- .../src/features/diagnostics/diagnostics.ts | 16 +- .../src/features/diagnostics/index.ts | 2 +- .../src/features/diagnostics/listing.tsx | 214 +++++++++++------- .../jupyterlab-lsp/src/features/highlights.ts | 2 +- .../jupyterlab-lsp/src/features/rename.ts | 2 +- .../src/features/syntax_highlighting.ts | 2 +- packages/jupyterlab-lsp/src/index.ts | 2 +- 10 files changed, 172 insertions(+), 115 deletions(-) diff --git a/packages/completion-theme/src/index.ts b/packages/completion-theme/src/index.ts index ae976191f..c7d1c4f88 100644 --- a/packages/completion-theme/src/index.ts +++ b/packages/completion-theme/src/index.ts @@ -182,7 +182,7 @@ export const COMPLETION_THEME_MANAGER: JupyterFrontEndPlugin { - const trans = translator.load('jupyterlab-lsp'); + const trans = translator.load('jupyterlab_lsp'); let manager = new CompletionThemeManager(themeManager, trans); const command_id = 'lsp:completer-about-themes'; app.commands.addCommand(command_id, { diff --git a/packages/jupyterlab-lsp/src/components/utils.spec.ts b/packages/jupyterlab-lsp/src/components/utils.spec.ts index 2bc738e69..3b1d52e52 100644 --- a/packages/jupyterlab-lsp/src/components/utils.spec.ts +++ b/packages/jupyterlab-lsp/src/components/utils.spec.ts @@ -1,10 +1,11 @@ import { IDocumentWidget } from '@jupyterlab/docregistry'; +import { nullTranslator } from '@jupyterlab/translation'; import { WidgetAdapter } from '../adapters/adapter'; import { BrowserConsole } from '../virtual/console'; import { VirtualDocument } from '../virtual/document'; -import { get_breadcrumbs } from './utils'; +import { getBreadcrumbs } from './utils'; function create_dummy_document(options: Partial) { return new VirtualDocument({ @@ -20,13 +21,18 @@ function create_dummy_document(options: Partial) { } describe('get_breadcrumbs', () => { + const trans = nullTranslator.load('jupyterlab_lsp'); it('should collapse long paths', () => { let document = create_dummy_document({ path: 'dir1/dir2/Test.ipynb' }); - let breadcrumbs = get_breadcrumbs(document, { - has_multiple_editors: false - } as WidgetAdapter); + let breadcrumbs = getBreadcrumbs( + document, + { + has_multiple_editors: false + } as WidgetAdapter, + trans + ); expect(breadcrumbs[0].props['title']).toBe('dir1/dir2/Test.ipynb'); expect(breadcrumbs[0].props['children']).toBe('dir1/.../Test.ipynb'); }); @@ -37,9 +43,13 @@ describe('get_breadcrumbs', () => { file_extension: 'py', has_lsp_supported_file: false }); - let breadcrumbs = get_breadcrumbs(document, { - has_multiple_editors: false - } as WidgetAdapter); + let breadcrumbs = getBreadcrumbs( + document, + { + has_multiple_editors: false + } as WidgetAdapter, + trans + ); expect(breadcrumbs[0].props['children']).toBe('Test.ipynb'); }); @@ -49,9 +59,13 @@ describe('get_breadcrumbs', () => { file_extension: 'py', has_lsp_supported_file: true }); - let breadcrumbs = get_breadcrumbs(document, { - has_multiple_editors: false - } as WidgetAdapter); + let breadcrumbs = getBreadcrumbs( + document, + { + has_multiple_editors: false + } as WidgetAdapter, + trans + ); expect(breadcrumbs[0].props['children']).toBe('test.py'); }); }); diff --git a/packages/jupyterlab-lsp/src/components/utils.tsx b/packages/jupyterlab-lsp/src/components/utils.tsx index b79c5988a..4607958ed 100644 --- a/packages/jupyterlab-lsp/src/components/utils.tsx +++ b/packages/jupyterlab-lsp/src/components/utils.tsx @@ -1,12 +1,14 @@ import { IDocumentWidget } from '@jupyterlab/docregistry'; +import { TranslationBundle } from '@jupyterlab/translation'; import React from 'react'; import { WidgetAdapter } from '../adapters/adapter'; import { VirtualDocument } from '../virtual/document'; -export function get_breadcrumbs( +export function getBreadcrumbs( document: VirtualDocument, adapter: WidgetAdapter, + trans: TranslationBundle, collapse = true ): JSX.Element[] { return document.ancestry.map((document: VirtualDocument) => { @@ -46,8 +48,8 @@ export function get_breadcrumbs( let cell_locator = first_cell === last_cell - ? `cell ${first_cell + 1}` - : `cells: ${first_cell + 1}-${last_cell + 1}`; + ? trans.__('cell %1', first_cell + 1) + : trans.__('cells: %1-%2', first_cell + 1, last_cell + 1); return ( @@ -73,6 +75,7 @@ export function focus_on(node: HTMLElement) { export function DocumentLocator(props: { document: VirtualDocument; adapter: WidgetAdapter; + trans?: TranslationBundle; }) { let { document, adapter } = props; let target: HTMLElement = null; @@ -84,7 +87,7 @@ export function DocumentLocator(props: { console.warn('Could not get first line of ', document); } } - let breadcrumbs = get_breadcrumbs(document, adapter); + let breadcrumbs = getBreadcrumbs(document, adapter, props.trans); return (
{ + let get_column = (id: string) => { // TODO: a hashmap in the panel itself? for (let column of widget.content.columns) { - if (column.name === name) { + if (column.id === id) { return column; } } @@ -110,13 +110,13 @@ class DiagnosticsPanel { app.commands.addCommand(CMD_COLUMN_VISIBILITY, { execute: args => { - let column = get_column(args['name'] as string); + let column = get_column(args['id'] as string); column.is_visible = !column.is_visible; widget.update(); }, - label: args => this.trans.__(`${args['name']}`) as string, + label: args => this.trans.__(args['id'] as string), isToggled: args => { - let column = get_column(args['name'] as string); + let column = get_column(args['id'] as string); return column.is_visible; } }); @@ -124,7 +124,7 @@ class DiagnosticsPanel { for (let column of widget.content.columns) { columns_menu.addItem({ command: CMD_COLUMN_VISIBILITY, - args: { name: column.name } + args: { id: column.id } }); } app.contextMenu.addItem({ @@ -238,7 +238,7 @@ class DiagnosticsPanel { .writeText(message) .then(() => { this.content.model.status_message.set( - this.trans.__(`Successfully copied "%1" to clipboard`, message) + this.trans.__('Successfully copied "%1" to clipboard', message) ); }) .catch(() => { diff --git a/packages/jupyterlab-lsp/src/features/diagnostics/index.ts b/packages/jupyterlab-lsp/src/features/diagnostics/index.ts index 8e4988331..152171e8c 100644 --- a/packages/jupyterlab-lsp/src/features/diagnostics/index.ts +++ b/packages/jupyterlab-lsp/src/features/diagnostics/index.ts @@ -56,7 +56,7 @@ export const DIAGNOSTICS_PLUGIN: JupyterFrontEndPlugin = { translator: ITranslator ) => { const settings = new FeatureSettings(settingRegistry, FEATURE_ID); - const trans = translator.load('jupyterlab-lsp'); + const trans = translator.load('jupyterlab_lsp'); featureManager.register({ feature: { diff --git a/packages/jupyterlab-lsp/src/features/diagnostics/listing.tsx b/packages/jupyterlab-lsp/src/features/diagnostics/listing.tsx index d6312679e..e1bfa282d 100644 --- a/packages/jupyterlab-lsp/src/features/diagnostics/listing.tsx +++ b/packages/jupyterlab-lsp/src/features/diagnostics/listing.tsx @@ -60,7 +60,8 @@ interface IListingContext { } interface IColumnOptions { - name: string; + id: string; + label: string; render_cell(data: IDiagnosticsRow, context?: IListingContext): ReactElement; sort(a: IDiagnosticsRow, b: IDiagnosticsRow): number; is_available?(context: IListingContext): boolean; @@ -81,8 +82,8 @@ class Column { return this.options.sort(a, b); } - get name(): string { - return this.options.name; + get id(): string { + return this.options.id; } is_available(context: IListingContext) { @@ -93,27 +94,35 @@ class Column { } render_header(listing: DiagnosticsListing): ReactElement { - return ; + return ( + + ); } } function SortableTH(props: { - name: string; + id: string; + label: string; listing: DiagnosticsListing; }): ReactElement { - const is_sort_key = props.name === props.listing.sort_key; + const is_sort_key = props.id === props.listing.sort_key; const sortIcon = !is_sort_key || props.listing.sort_direction === 1 ? caretUpIcon : caretDownIcon; return ( props.listing.sort(props.name)} + key={props.id} + onClick={() => props.listing.sort(props.id)} className={is_sort_key ? 'lsp-sorted-header' : null} >
- +
@@ -137,80 +146,112 @@ export class DiagnosticsListing extends VDomRenderer { sort_key = 'Severity'; sort_direction = 1; private _diagnostics: Map; + protected trans: TranslationBundle; + protected columns: Column[]; + protected severityTranslations: Record< + keyof typeof DiagnosticSeverity, + string + >; + + constructor(model: DiagnosticsListing.Model) { + super(model); + const trans = model.trans; + this.trans = trans; + this.severityTranslations = { + Error: trans.__('Error'), + Warning: trans.__('Warning'), + Information: trans.__('Information'), + Hint: trans.__('Hint') + }; - columns = [ - new Column({ - name: 'Virtual Document', - render_cell: (row, context: IListingContext) => ( - - - - ), - sort: (a, b) => a.document.id_path.localeCompare(b.document.id_path), - is_available: context => context.db.size > 1 - }), - new Column({ - name: 'Message', - render_cell: row => { - let message = message_without_code(row.data.diagnostic); - return {message}; - }, - sort: (a, b) => - a.data.diagnostic.message.localeCompare(b.data.diagnostic.message) - }), - new Column({ - name: 'Code', - render_cell: row => {row.data.diagnostic.code}, - sort: (a, b) => - (a.data.diagnostic.code + '').localeCompare( - b.data.diagnostic.source + '' - ) - }), - new Column({ - name: 'Severity', - // TODO: use default diagnostic severity - render_cell: row => ( - - {trans.__(DiagnosticSeverity[row.data.diagnostic.severity || 1])} - - ), - sort: (a, b) => - a.data.diagnostic.severity > b.data.diagnostic.severity ? 1 : -1 - }), - new Column({ - name: 'Source', - render_cell: row => {row.data.diagnostic.source}, - sort: (a, b) => - a.data.diagnostic.source.localeCompare(b.data.diagnostic.source) - }), - new Column({ - name: 'Cell', - render_cell: row => {row.cell_number}, - sort: (a, b) => - a.cell_number > b.cell_number - ? 1 - : a.data.range.start.line > b.data.range.start.line - ? 1 - : a.data.range.start.ch > b.data.range.start.ch - ? 1 - : -1, - is_available: context => context.adapter.has_multiple_editors - }), - new Column({ - name: 'Line:Ch', - render_cell: row => ( - - {row.data.range.start.line}:{row.data.range.start.ch} - - ), - sort: (a, b) => - a.data.range.start.line > b.data.range.start.line - ? 1 - : a.data.range.start.ch > b.data.range.start.ch - ? 1 - : -1 - }) - ]; + this.columns = [ + new Column({ + id: 'Virtual Document', + label: this.trans.__('Virtual Document'), + render_cell: (row, context: IListingContext) => ( + + + + ), + sort: (a, b) => a.document.id_path.localeCompare(b.document.id_path), + is_available: context => context.db.size > 1 + }), + new Column({ + id: 'Message', + label: this.trans.__('Message'), + render_cell: row => { + let message = message_without_code(row.data.diagnostic); + return {message}; + }, + sort: (a, b) => + a.data.diagnostic.message.localeCompare(b.data.diagnostic.message) + }), + new Column({ + id: 'Code', + label: this.trans.__('Code'), + render_cell: row => {row.data.diagnostic.code}, + sort: (a, b) => + (a.data.diagnostic.code + '').localeCompare( + b.data.diagnostic.source + '' + ) + }), + new Column({ + id: 'Severity', + label: this.trans.__('Severity'), + // TODO: use default diagnostic severity + render_cell: row => { + const severity = DiagnosticSeverity[ + row.data.diagnostic.severity || 1 + ] as keyof typeof DiagnosticSeverity; + return ( + {this.severityTranslations[severity] || severity} + ); + }, + sort: (a, b) => + a.data.diagnostic.severity > b.data.diagnostic.severity ? 1 : -1 + }), + new Column({ + id: 'Source', + label: this.trans.__('Source'), + render_cell: row => {row.data.diagnostic.source}, + sort: (a, b) => + a.data.diagnostic.source.localeCompare(b.data.diagnostic.source) + }), + new Column({ + id: 'Cell', + label: this.trans.__('Cell'), + render_cell: row => {row.cell_number}, + sort: (a, b) => + a.cell_number > b.cell_number + ? 1 + : a.data.range.start.line > b.data.range.start.line + ? 1 + : a.data.range.start.ch > b.data.range.start.ch + ? 1 + : -1, + is_available: context => context.adapter.has_multiple_editors + }), + new Column({ + id: 'Line:Ch', + label: this.trans.__('Line:Ch'), + render_cell: row => ( + + {row.data.range.start.line}:{row.data.range.start.ch} + + ), + sort: (a, b) => + a.data.range.start.line > b.data.range.start.line + ? 1 + : a.data.range.start.ch > b.data.range.start.ch + ? 1 + : -1 + }) + ]; + } sort(key: string) { if (key === this.sort_key) { @@ -227,7 +268,7 @@ export class DiagnosticsListing extends VDomRenderer { const editor = this.model.virtual_editor; const adapter = this.model.adapter; if (!diagnostics_db || editor == null) { - return
{trans.__('No issues detected, great job!')}
; + return
{this.trans.__('No issues detected, great job!')}
; } let by_document = Array.from(diagnostics_db).map( @@ -255,7 +296,7 @@ export class DiagnosticsListing extends VDomRenderer { this._diagnostics = new Map(flattened.map(row => [row.key, row])); let sorted_column = this.columns.filter( - column => column.name === this.sort_key + column => column.id === this.sort_key )[0]; let sorter = sorted_column.sort.bind(sorted_column); let sorted = flattened.sort((a, b) => sorter(a, b) * this.sort_direction); @@ -318,8 +359,6 @@ export class DiagnosticsListing extends VDomRenderer { } } -let trans: TranslationBundle; - export namespace DiagnosticsListing { /** * A VDomModel for the LSP of current file editor/notebook. @@ -330,10 +369,11 @@ export namespace DiagnosticsListing { adapter: WidgetAdapter; settings: FeatureSettings; status_message: StatusMessage; + trans: TranslationBundle; constructor(translator_bundle: TranslationBundle) { super(); - trans = translator_bundle; + this.trans = translator_bundle; } } } diff --git a/packages/jupyterlab-lsp/src/features/highlights.ts b/packages/jupyterlab-lsp/src/features/highlights.ts index 2c9f62139..043e2f582 100644 --- a/packages/jupyterlab-lsp/src/features/highlights.ts +++ b/packages/jupyterlab-lsp/src/features/highlights.ts @@ -246,7 +246,7 @@ export const HIGHLIGHTS_PLUGIN: JupyterFrontEndPlugin = { translator: ITranslator ) => { const settings = new FeatureSettings(settingRegistry, FEATURE_ID); - const trans = translator.load('jupyterlab-lsp'); + const trans = translator.load('jupyterlab_lsp'); featureManager.register({ feature: { diff --git a/packages/jupyterlab-lsp/src/features/rename.ts b/packages/jupyterlab-lsp/src/features/rename.ts index f772aafd5..d90448cbe 100644 --- a/packages/jupyterlab-lsp/src/features/rename.ts +++ b/packages/jupyterlab-lsp/src/features/rename.ts @@ -247,7 +247,7 @@ export const RENAME_PLUGIN: JupyterFrontEndPlugin = { settingRegistry: ISettingRegistry, translator: ITranslator ) => { - const trans = (translator || nullTranslator).load('jupyterlab-lsp'); + const trans = (translator || nullTranslator).load('jupyterlab_lsp'); const settings = new FeatureSettings(settingRegistry, FEATURE_ID); featureManager.register({ diff --git a/packages/jupyterlab-lsp/src/features/syntax_highlighting.ts b/packages/jupyterlab-lsp/src/features/syntax_highlighting.ts index 17bd3cf8e..9a0ab7938 100644 --- a/packages/jupyterlab-lsp/src/features/syntax_highlighting.ts +++ b/packages/jupyterlab-lsp/src/features/syntax_highlighting.ts @@ -138,7 +138,7 @@ export const SYNTAX_HIGHLIGHTING_PLUGIN: JupyterFrontEndPlugin = { translator: ITranslator ) => { const settings = new FeatureSettings(settingRegistry, FEATURE_ID); - const trans = translator.load('jupyterlab-lsp'); + const trans = translator.load('jupyterlab_lsp'); featureManager.register({ feature: { diff --git a/packages/jupyterlab-lsp/src/index.ts b/packages/jupyterlab-lsp/src/index.ts index fc2f3d559..20f98aeb6 100644 --- a/packages/jupyterlab-lsp/src/index.ts +++ b/packages/jupyterlab-lsp/src/index.ts @@ -149,7 +149,7 @@ export class LSPExtension implements ILSPExtension { public user_console: ILoggerRegistry, status_bar: IStatusBar | null ) { - const trans = (translator || nullTranslator).load('jupyterlab-lsp'); + const trans = (translator || nullTranslator).load('jupyterlab_lsp'); this.language_server_manager = new LanguageServerManager({ console: this.console.scope('LanguageServerManager') });