mirror of
https://github.com/enso-org/enso.git
synced 2024-11-26 08:52:58 +03:00
CB Fixes and Improvements (#8385)
* The "main view" (when no self type/module nor pattern is specified) contains all methods defined in top modules of every library * Entries defined inside any "Internal" module from stdlib will be hidden by default; they will be visible only when self type is specified or the module path leading to such module internals. * Fixed an issue with missing groups sometimes - we must wait with asking for groups for first executionComplete message. * Modules and local variables have different default icon. ![image](https://github.com/enso-org/enso/assets/3919101/cb33691e-222b-413e-a92e-2cf84e9fe4bb) ![image](https://github.com/enso-org/enso/assets/3919101/beab202d-4feb-4b00-ba0c-c141862da53c)
This commit is contained in:
parent
c889c8e83f
commit
a38680adf4
File diff suppressed because it is too large
Load Diff
Before Width: | Height: | Size: 106 KiB After Width: | Height: | Size: 148 KiB |
@ -353,7 +353,7 @@ const handler = componentBrowserBindings.handler({
|
|||||||
<div class="top-bar">
|
<div class="top-bar">
|
||||||
<div class="top-bar-inner">
|
<div class="top-bar-inner">
|
||||||
<ToggleIcon v-model="filterFlags.showLocal" icon="local_scope2" />
|
<ToggleIcon v-model="filterFlags.showLocal" icon="local_scope2" />
|
||||||
<ToggleIcon icon="command_key3" />
|
<ToggleIcon icon="command3" />
|
||||||
<ToggleIcon v-model="filterFlags.showUnstable" icon="unstable2" />
|
<ToggleIcon v-model="filterFlags.showUnstable" icon="unstable2" />
|
||||||
<ToggleIcon icon="marketplace" />
|
<ToggleIcon icon="marketplace" />
|
||||||
<ToggleIcon v-model="docsVisible" icon="right_side_panel" class="first-on-right" />
|
<ToggleIcon v-model="docsVisible" icon="right_side_panel" class="first-on-right" />
|
||||||
|
@ -20,15 +20,21 @@ test.each([
|
|||||||
{ ...makeStaticMethod('Standard.Base.Data.Vector.Vector.new'), groupIndex: 1 },
|
{ ...makeStaticMethod('Standard.Base.Data.Vector.Vector.new'), groupIndex: 1 },
|
||||||
makeModule('local.New_Project'),
|
makeModule('local.New_Project'),
|
||||||
makeModule('Standard.Base.Data'),
|
makeModule('Standard.Base.Data'),
|
||||||
|
makeModuleMethod('Standard.Base.Data.read_text'),
|
||||||
|
makeStaticMethod('local.Project.Foo.new'),
|
||||||
|
makeStaticMethod('local.Project.Internalization.internalize'),
|
||||||
])('$name entry is in the CB main view', (entry) => {
|
])('$name entry is in the CB main view', (entry) => {
|
||||||
const filtering = new Filtering({})
|
const filtering = new Filtering({})
|
||||||
expect(filtering.filter(entry)).not.toBeNull()
|
expect(filtering.filter(entry)).not.toBeNull()
|
||||||
})
|
})
|
||||||
|
|
||||||
test.each([
|
test.each([
|
||||||
makeModuleMethod('Standard.Base.Data.convert'), // not in group
|
makeModuleMethod('Standard.Base.Data.Vector.some_method'), // not in top group
|
||||||
{ ...makeMethod('Standard.Base.Data.Vector.Vector.get'), groupIndex: 1 }, // not static method
|
{ ...makeMethod('Standard.Base.Data.Vector.Vector.get'), groupIndex: 1 }, // not static method
|
||||||
makeModule('Standard.Base.Data.Vector'), // Not top module
|
makeModule('Standard.Base.Data.Vector'), // Not top module
|
||||||
|
makeStaticMethod('Standard.Base.Internal.Foo.bar'), // Internal method
|
||||||
|
makeModule('Standard.Base.Internal'), // Internal module
|
||||||
|
makeModule('Standard.Internal.Foo'), // Internal project
|
||||||
])('$name entry is not in the CB main view', (entry) => {
|
])('$name entry is not in the CB main view', (entry) => {
|
||||||
const filtering = new Filtering({})
|
const filtering = new Filtering({})
|
||||||
expect(filtering.filter(entry)).toBeNull()
|
expect(filtering.filter(entry)).toBeNull()
|
||||||
@ -63,6 +69,18 @@ test.each([
|
|||||||
expect(substringFiltering.filter(entry)).toBeNull()
|
expect(substringFiltering.filter(entry)).toBeNull()
|
||||||
})
|
})
|
||||||
|
|
||||||
|
test('Internal entry is not in the Standard.Base.Data content', () => {
|
||||||
|
const entry = makeModule('Standard.Base.Data.Internal')
|
||||||
|
const filtering = new Filtering({ qualifiedNamePattern: 'Standard.Base.Data' })
|
||||||
|
expect(filtering.filter(entry)).toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
|
test('The content of an internal module is displayed', () => {
|
||||||
|
const entry = makeModuleMethod('Standard.Base.Data.Internal.foo')
|
||||||
|
const filtering = new Filtering({ qualifiedNamePattern: 'Standard.Base.Data.Internal' })
|
||||||
|
expect(filtering.filter(entry)).not.toBeNull()
|
||||||
|
})
|
||||||
|
|
||||||
test.each([
|
test.each([
|
||||||
makeModuleMethod('local.Project.Module.foo'),
|
makeModuleMethod('local.Project.Module.foo'),
|
||||||
makeModuleMethod('local.Project.Module.Submodule.foo_in_submodule'),
|
makeModuleMethod('local.Project.Module.Submodule.foo_in_submodule'),
|
||||||
|
@ -285,6 +285,7 @@ export class Filtering {
|
|||||||
showUnstable: boolean = false
|
showUnstable: boolean = false
|
||||||
showLocal: boolean = false
|
showLocal: boolean = false
|
||||||
currentModule?: QualifiedName
|
currentModule?: QualifiedName
|
||||||
|
browsingInternalModule: boolean = false
|
||||||
|
|
||||||
constructor(filter: Filter, currentModule: Opt<QualifiedName> = undefined) {
|
constructor(filter: Filter, currentModule: Opt<QualifiedName> = undefined) {
|
||||||
const { pattern, selfArg, qualifiedNamePattern, showUnstable, showLocal } = filter
|
const { pattern, selfArg, qualifiedNamePattern, showUnstable, showLocal } = filter
|
||||||
@ -295,6 +296,7 @@ export class Filtering {
|
|||||||
if (qualifiedNamePattern) {
|
if (qualifiedNamePattern) {
|
||||||
this.qualifiedName = new FilteringQualifiedName(qualifiedNamePattern)
|
this.qualifiedName = new FilteringQualifiedName(qualifiedNamePattern)
|
||||||
this.fullPattern = pattern ? `${qualifiedNamePattern}.${pattern}` : qualifiedNamePattern
|
this.fullPattern = pattern ? `${qualifiedNamePattern}.${pattern}` : qualifiedNamePattern
|
||||||
|
this.browsingInternalModule = isInternalModulePath(qualifiedNamePattern)
|
||||||
} else if (pattern) this.fullPattern = pattern
|
} else if (pattern) this.fullPattern = pattern
|
||||||
if (this.fullPattern) {
|
if (this.fullPattern) {
|
||||||
let prefix = ''
|
let prefix = ''
|
||||||
@ -334,9 +336,11 @@ export class Filtering {
|
|||||||
private mainViewFilter(entry: SuggestionEntry): MatchResult | null {
|
private mainViewFilter(entry: SuggestionEntry): MatchResult | null {
|
||||||
const hasGroup = entry.groupIndex != null
|
const hasGroup = entry.groupIndex != null
|
||||||
const isModule = entry.kind === SuggestionKind.Module
|
const isModule = entry.kind === SuggestionKind.Module
|
||||||
const isTopElement = qnIsTopElement(entry.definedIn)
|
const isMethod = entry.kind === SuggestionKind.Method
|
||||||
if (!hasGroup && (!isModule || !isTopElement)) return null
|
const isInTopModule = qnIsTopElement(entry.definedIn)
|
||||||
else return { score: 0 }
|
const isTopElementInMainView = (isMethod || isModule) && isInTopModule
|
||||||
|
if (hasGroup || isTopElementInMainView) return { score: 0 }
|
||||||
|
else return null
|
||||||
}
|
}
|
||||||
|
|
||||||
private isLocal(entry: SuggestionEntry): boolean {
|
private isLocal(entry: SuggestionEntry): boolean {
|
||||||
@ -344,18 +348,28 @@ export class Filtering {
|
|||||||
}
|
}
|
||||||
|
|
||||||
filter(entry: SuggestionEntry): MatchResult | null {
|
filter(entry: SuggestionEntry): MatchResult | null {
|
||||||
let qualifiedNameMatch: Opt<MatchedParts>
|
|
||||||
if (entry.isPrivate) return null
|
if (entry.isPrivate) return null
|
||||||
else if (!this.selfTypeMatches(entry)) return null
|
if (this.selfArg == null && isInternal(entry) && !this.browsingInternalModule) return null
|
||||||
else if (!(qualifiedNameMatch = this.qualifiedNameMatches(entry))) return null
|
if (!this.selfTypeMatches(entry)) return null
|
||||||
else if (!this.showUnstable && entry.isUnstable) return null
|
const qualifiedNameMatch = this.qualifiedNameMatches(entry)
|
||||||
else if (this.showLocal && !this.isLocal(entry)) return null
|
if (!qualifiedNameMatch) return null
|
||||||
else if (this.pattern) {
|
if (!this.showUnstable && entry.isUnstable) return null
|
||||||
|
if (this.showLocal && !this.isLocal(entry)) return null
|
||||||
|
if (this.pattern) {
|
||||||
const patternMatch = this.pattern.tryMatch(entry)
|
const patternMatch = this.pattern.tryMatch(entry)
|
||||||
if (!patternMatch) return null
|
if (!patternMatch) return null
|
||||||
if (!this.showLocal && this.isLocal(entry)) patternMatch.score *= 2
|
if (!this.showLocal && this.isLocal(entry)) patternMatch.score *= 2
|
||||||
return { ...qualifiedNameMatch, ...patternMatch }
|
return { ...qualifiedNameMatch, ...patternMatch }
|
||||||
} else if (this.isMainView()) return this.mainViewFilter(entry)
|
}
|
||||||
else return { score: 0 }
|
if (this.isMainView()) return this.mainViewFilter(entry)
|
||||||
|
return { score: 0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
function isInternal(entry: SuggestionEntry): boolean {
|
||||||
|
return isInternalModulePath(entry.definedIn)
|
||||||
|
}
|
||||||
|
|
||||||
|
function isInternalModulePath(path: string): boolean {
|
||||||
|
return /Standard[.].*Internal(?:[._]|$)/.test(path)
|
||||||
|
}
|
||||||
|
@ -9,6 +9,7 @@ import {
|
|||||||
createWebsocketClient,
|
createWebsocketClient,
|
||||||
rpcWithRetries as lsRpcWithRetries,
|
rpcWithRetries as lsRpcWithRetries,
|
||||||
} from '@/util/net'
|
} from '@/util/net'
|
||||||
|
import { nextEvent } from '@/util/observable'
|
||||||
import { isSome, type Opt } from '@/util/opt'
|
import { isSome, type Opt } from '@/util/opt'
|
||||||
import { tryQualifiedName } from '@/util/qualifiedName'
|
import { tryQualifiedName } from '@/util/qualifiedName'
|
||||||
import { VisualizationDataRegistry } from '@/util/visualizationDataRegistry'
|
import { VisualizationDataRegistry } from '@/util/visualizationDataRegistry'
|
||||||
@ -518,6 +519,9 @@ export const useProjectStore = defineStore('project', () => {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const firstExecution = lsRpcConnection.then((lsRpc) =>
|
||||||
|
nextEvent(lsRpc, 'executionContext/executionComplete'),
|
||||||
|
)
|
||||||
const executionContext = createExecutionContextForMain()
|
const executionContext = createExecutionContextForMain()
|
||||||
const computedValueRegistry = ComputedValueRegistry.WithExecutionContext(executionContext)
|
const computedValueRegistry = ComputedValueRegistry.WithExecutionContext(executionContext)
|
||||||
const visualizationDataRegistry = new VisualizationDataRegistry(executionContext, dataConnection)
|
const visualizationDataRegistry = new VisualizationDataRegistry(executionContext, dataConnection)
|
||||||
@ -563,6 +567,7 @@ export const useProjectStore = defineStore('project', () => {
|
|||||||
},
|
},
|
||||||
name: projectName,
|
name: projectName,
|
||||||
executionContext,
|
executionContext,
|
||||||
|
firstExecution,
|
||||||
diagnostics,
|
diagnostics,
|
||||||
module,
|
module,
|
||||||
modulePath,
|
modulePath,
|
||||||
|
@ -76,6 +76,7 @@ class Synchronizer {
|
|||||||
lsRpc.acquireCapability('search/receivesSuggestionsDatabaseUpdates', {}),
|
lsRpc.acquireCapability('search/receivesSuggestionsDatabaseUpdates', {}),
|
||||||
)
|
)
|
||||||
this.setupUpdateHandler(lsRpc)
|
this.setupUpdateHandler(lsRpc)
|
||||||
|
this.loadGroups(lsRpc, projectStore.firstExecution)
|
||||||
return Synchronizer.loadDatabase(entries, lsRpc, groups.value)
|
return Synchronizer.loadDatabase(entries, lsRpc, groups.value)
|
||||||
})
|
})
|
||||||
|
|
||||||
@ -125,7 +126,11 @@ class Synchronizer {
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
})
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
private async loadGroups(lsRpc: LanguageServer, firstExecution: Promise<unknown>) {
|
||||||
this.queue.pushTask(async ({ currentVersion }) => {
|
this.queue.pushTask(async ({ currentVersion }) => {
|
||||||
|
await firstExecution
|
||||||
const groups = await lsRpc.getComponentGroups()
|
const groups = await lsRpc.getComponentGroups()
|
||||||
this.groups.value = groups.componentGroups.map(
|
this.groups.value = groups.componentGroups.map(
|
||||||
(group): Group => ({
|
(group): Group => ({
|
||||||
|
@ -1,6 +1,5 @@
|
|||||||
import type { Opt } from '@/util/opt'
|
import type { Opt } from '@/util/opt'
|
||||||
import { Vec2 } from '@/util/vec2'
|
import { Vec2 } from '@/util/vec2'
|
||||||
import type { ObservableV2 } from 'lib0/observable'
|
|
||||||
import {
|
import {
|
||||||
computed,
|
computed,
|
||||||
onScopeDispose,
|
onScopeDispose,
|
||||||
@ -12,7 +11,6 @@ import {
|
|||||||
type Ref,
|
type Ref,
|
||||||
type WatchSource,
|
type WatchSource,
|
||||||
} from 'vue'
|
} from 'vue'
|
||||||
import { ReactiveDb } from './database/reactiveDb'
|
|
||||||
|
|
||||||
export function isClick(e: MouseEvent | PointerEvent) {
|
export function isClick(e: MouseEvent | PointerEvent) {
|
||||||
if (e instanceof PointerEvent) return e.pointerId !== -1
|
if (e instanceof PointerEvent) return e.pointerId !== -1
|
||||||
|
@ -1,13 +1,17 @@
|
|||||||
import type { SuggestionEntry, Typename } from '@/stores/suggestionDatabase/entry'
|
import {
|
||||||
|
SuggestionKind,
|
||||||
|
type SuggestionEntry,
|
||||||
|
type Typename,
|
||||||
|
} from '@/stores/suggestionDatabase/entry'
|
||||||
import type { Icon } from '@/util/iconName'
|
import type { Icon } from '@/util/iconName'
|
||||||
import type { MethodPointer } from 'shared/languageServerTypes'
|
import type { MethodPointer } from 'shared/languageServerTypes'
|
||||||
|
|
||||||
const oldIconNameToNewIconNameLookup: Record<string, string> = {
|
const oldIconNameToNewIconNameLookup: Record<string, Icon> = {
|
||||||
/* eslint-disable camelcase */
|
/* eslint-disable camelcase */
|
||||||
dataframe_clean: 'clean_dataframe',
|
dataframe_clean: 'table_clean',
|
||||||
dataframe_map_row: 'map_row',
|
dataframe_map_row: 'map_row',
|
||||||
dataframe_map_column: 'add_column',
|
dataframe_map_column: 'column_add',
|
||||||
dataframes_join: 'join3',
|
dataframes_join: 'join2-1',
|
||||||
dataframes_union: 'union',
|
dataframes_union: 'union',
|
||||||
sigma: 'transform4',
|
sigma: 'transform4',
|
||||||
io: 'in_out',
|
io: 'in_out',
|
||||||
@ -25,8 +29,8 @@ export function mapOldIconName(oldIconName: string): Icon {
|
|||||||
|
|
||||||
const typeNameToIconLookup: Record<string, Icon> = {
|
const typeNameToIconLookup: Record<string, Icon> = {
|
||||||
'Standard.Base.Data.Text.Text': 'text_input',
|
'Standard.Base.Data.Text.Text': 'text_input',
|
||||||
'Standard.Base.Data.Numbers.Integer': 'number_input',
|
'Standard.Base.Data.Numbers.Integer': 'input_number',
|
||||||
'Standard.Base.Data.Numbers.Float': 'number_input',
|
'Standard.Base.Data.Numbers.Float': 'input_number',
|
||||||
'Standard.Base.Data.Array.Array': 'array_new',
|
'Standard.Base.Data.Array.Array': 'array_new',
|
||||||
'Standard.Base.Data.Vector.Vector': 'array_new',
|
'Standard.Base.Data.Vector.Vector': 'array_new',
|
||||||
'Standard.Base.Data.Time.Date.Date': 'calendar',
|
'Standard.Base.Data.Time.Date.Date': 'calendar',
|
||||||
@ -43,7 +47,10 @@ export function displayedIconOf(
|
|||||||
methodCall?: MethodPointer,
|
methodCall?: MethodPointer,
|
||||||
actualType?: Typename,
|
actualType?: Typename,
|
||||||
): Icon {
|
): Icon {
|
||||||
if (entry?.iconName) return mapOldIconName(entry.iconName)
|
if (entry) {
|
||||||
if (!methodCall?.name && actualType) return typeNameToIcon(actualType)
|
if (entry.iconName) return mapOldIconName(entry.iconName)
|
||||||
|
if (entry.kind === SuggestionKind.Local) return 'local_scope2'
|
||||||
|
if (entry.kind === SuggestionKind.Module) return 'collection'
|
||||||
|
} else if (!methodCall?.name && actualType) return typeNameToIcon(actualType)
|
||||||
return 'enso_logo'
|
return 'enso_logo'
|
||||||
}
|
}
|
||||||
|
24
app/gui2/src/util/observable.ts
Normal file
24
app/gui2/src/util/observable.ts
Normal file
@ -0,0 +1,24 @@
|
|||||||
|
import type { ObservableV2 } from 'lib0/observable'
|
||||||
|
|
||||||
|
export type Events<O extends ObservableV2<any>> = O extends ObservableV2<infer E> ? E : never
|
||||||
|
|
||||||
|
/** Returns promise which will resolve on the next event. The promise will have the event's
|
||||||
|
* payload. */
|
||||||
|
export function nextEvent<O extends ObservableV2<any>, NAME extends string>(
|
||||||
|
observable: O,
|
||||||
|
event: NAME,
|
||||||
|
): Promise<Parameters<Events<O>[NAME]>> {
|
||||||
|
type Params = Parameters<Events<O>[NAME]>
|
||||||
|
return new Promise<Params>((resolve) => {
|
||||||
|
observable.once(event, (...args: Params) => {
|
||||||
|
resolve(args)
|
||||||
|
})
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
declare const EVENTS_BRAND: unique symbol
|
||||||
|
declare module 'lib0/observable' {
|
||||||
|
interface ObservableV2<EVENTS extends { [key: string]: (...arg0: any[]) => void }> {
|
||||||
|
[EVENTS_BRAND]: EVENTS
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in New Issue
Block a user