diff --git a/app/gui2/src/components/ComponentBrowser.vue b/app/gui2/src/components/ComponentBrowser.vue index 3923ad782291..9463c1ee5218 100644 --- a/app/gui2/src/components/ComponentBrowser.vue +++ b/app/gui2/src/components/ComponentBrowser.vue @@ -5,6 +5,7 @@ import { Filtering } from '@/components/ComponentBrowser/filtering' import { Input } from '@/components/ComponentBrowser/input' import SvgIcon from '@/components/SvgIcon.vue' import ToggleIcon from '@/components/ToggleIcon.vue' +import { useProjectStore } from '@/stores/project' import { useSuggestionDbStore } from '@/stores/suggestionDatabase' import { SuggestionKind, type SuggestionEntry } from '@/stores/suggestionDatabase/entry' import { useApproach } from '@/util/animation' @@ -35,6 +36,8 @@ onMounted(() => { } }) +const projectStore = useProjectStore() + // === Position === const transform = computed(() => { @@ -53,10 +56,14 @@ const input = new Input() const filterFlags = ref({ showUnstable: false, showLocal: false }) const currentFiltering = computed(() => { - return new Filtering({ - ...input.filter.value, - ...filterFlags.value, - }) + const currentModule = projectStore.modulePath + return new Filtering( + { + ...input.filter.value, + ...filterFlags.value, + }, + currentModule?.ok ? currentModule.value : undefined, + ) }) watch(currentFiltering, selectLastAfterRefresh) diff --git a/app/gui2/src/components/ComponentBrowser/__tests__/filtering.test.ts b/app/gui2/src/components/ComponentBrowser/__tests__/filtering.test.ts index 28ac44c2bf5f..a9830df7f46c 100644 --- a/app/gui2/src/components/ComponentBrowser/__tests__/filtering.test.ts +++ b/app/gui2/src/components/ComponentBrowser/__tests__/filtering.test.ts @@ -11,7 +11,8 @@ import { makeStaticMethod, makeType, } from '@/stores/suggestionDatabase/entry' -import type { QualifiedName } from '@/util/qualifiedName' +import { tryQualifiedName, type QualifiedName } from '@/util/qualifiedName' +import { unwrap } from '@/util/result' test.each([ { ...makeModuleMethod('Standard.Base.Data.read'), groupIndex: 0 }, @@ -267,3 +268,30 @@ test('Unstable filtering', () => { expect(unstableFiltering.filter(stableEntry)).not.toBeNull() expect(unstableFiltering.filter(unstableEntry)).not.toBeNull() }) + +test.each([ + makeModuleMethod('local.Project.Module.func1'), + makeType('local.Project.Module.Type'), + makeCon('local.Project.Module.Type.Con'), + makeStaticMethod('local.Project.Module.Type.method'), + makeLocal('local.Project.Module', 'operator1'), + makeFunction('local.Project.Module', 'func2'), +])('$name entry is in Local Scope view', (entry) => { + const filtering = new Filtering( + { showLocal: true }, + unwrap(tryQualifiedName('local.Project.Module')), + ) + expect(filtering.filter(entry)).not.toBeNull() +}) + +test.each([ + makeModuleMethod('Standard.Base.Data.read'), + makeLocal('local.Project.Another_Module', 'local_in_another_module'), + makeFunction('local.Project.Another_Module', 'function_in_another_module'), +])('$name entry is not in Local Scope View', (entry) => { + const filtering = new Filtering( + { showLocal: true }, + unwrap(tryQualifiedName('local.Project.Module')), + ) + expect(filtering.filter(entry)).toBeNull() +}) diff --git a/app/gui2/src/components/ComponentBrowser/filtering.ts b/app/gui2/src/components/ComponentBrowser/filtering.ts index 4aa786f4394c..33f26f2f9431 100644 --- a/app/gui2/src/components/ComponentBrowser/filtering.ts +++ b/app/gui2/src/components/ComponentBrowser/filtering.ts @@ -240,7 +240,8 @@ class FilteringQualifiedName { * * - Without `showUnstable` flag, unstable entries will be filtered out. * - * - 'showLocal' flag is not implemented yet. + * - If 'showLocal' flag is set, only entries defined in currentModule (passed as constructor + * argument) are accepted. * * - Finally, if `pattern` is specified, the entry name or any alias must match the pattern: * there must exists a subsequence of words in name/alias (words are separated by `_`), so each @@ -271,8 +272,9 @@ export class Filtering { extractMatchesRegex: RegExp | undefined showUnstable: boolean = false showLocal: boolean = false + currentModule?: QualifiedName - constructor(filter: Filter) { + constructor(filter: Filter, currentModule: Opt = undefined) { const { pattern, selfType, qualifiedNamePattern, showUnstable, showLocal } = filter if (pattern) { this.pattern = new FilteringWithPattern(pattern) @@ -297,6 +299,7 @@ export class Filtering { } this.showUnstable = showUnstable ?? false this.showLocal = showLocal ?? false + if (currentModule != null) this.currentModule = currentModule } private selfTypeMatches(entry: SuggestionEntry): boolean { @@ -332,6 +335,11 @@ export class Filtering { else if (!this.selfTypeMatches(entry)) return null else if (!(qualifiedNameMatch = this.qualifiedNameMatches(entry))) return null else if (!this.showUnstable && entry.isUnstable) return null + else if ( + this.showLocal && + (this.currentModule == null || entry.definedIn !== this.currentModule) + ) + return null else if (this.pattern) { const patternMatch = this.pattern.tryMatch(entry) if (!patternMatch || !qualifiedNameMatch) return patternMatch diff --git a/app/gui2/src/providers/guiConfig.ts b/app/gui2/src/providers/guiConfig.ts index 585b532e97fc..80d5fa928ea4 100644 --- a/app/gui2/src/providers/guiConfig.ts +++ b/app/gui2/src/providers/guiConfig.ts @@ -10,6 +10,7 @@ export interface GuiConfig { } startup?: { project?: string + displayedProjectName: string } window?: { topBarOffset?: string } } diff --git a/app/gui2/src/stores/project.ts b/app/gui2/src/stores/project.ts index 1f56d8ec3ecf..cb47f243a301 100644 --- a/app/gui2/src/stores/project.ts +++ b/app/gui2/src/stores/project.ts @@ -3,14 +3,17 @@ import { ComputedValueRegistry } from '@/util/computedValueRegistry' import { attachProvider } from '@/util/crdt' import { AsyncQueue, rpcWithRetries as lsRpcWithRetries } from '@/util/net' import { isSome, type Opt } from '@/util/opt' +import { tryQualifiedName } from '@/util/qualifiedName' import { VisualizationDataRegistry } from '@/util/visualizationDataRegistry' import { Client, RequestManager, WebSocketTransport } from '@open-rpc/client-js' import { computedAsync } from '@vueuse/core' +import { namespace } from 'd3' import * as array from 'lib0/array' import * as object from 'lib0/object' import { ObservableV2 } from 'lib0/observable' import * as random from 'lib0/random' import { defineStore } from 'pinia' +import { mainModule } from 'process' import { DataServer } from 'shared/dataServer' import { LanguageServer } from 'shared/languageServer' import type { @@ -412,6 +415,30 @@ export const useProjectStore = defineStore('project', () => { const name = computed(() => config.value.startup?.project) const namespace = computed(() => config.value.engine?.namespace) + const fullName = computed(() => { + const ns = namespace.value + if (ns == null) { + console.warn( + 'Unknown project\'s namespace. Assuming "local", however it likely won\'t work in cloud', + ) + } + const projectName = name.value + if (projectName == null) { + console.error( + "Unknown project's name. Cannot specify opened module's qualified path; many things may not work", + ) + return null + } + return `${ns ?? 'local'}.${projectName}` + }) + const modulePath = computed(() => { + const filePath = observedFileName.value + console.log(filePath) + if (filePath == null) return undefined + const withoutFileExt = filePath.replace(/\.enso$/, '') + const withDotSeparators = withoutFileExt.replace(/\//g, '.') + return tryQualifiedName(`${fullName.value}.${withDotSeparators}`) + }) watchEffect((onCleanup) => { // For now, let's assume that the websocket server is running on the same host as the web server. @@ -453,15 +480,7 @@ export const useProjectStore = defineStore('project', () => { }) function createExecutionContextForMain(): ExecutionContext { - if (name.value == null) { - throw new Error('Cannot create execution context. Unknown project name.') - } - if (namespace.value == null) { - console.warn( - 'Unknown project\'s namespace. Assuming "local", however it likely won\'t work in cloud', - ) - } - const projectName = `${namespace.value ?? 'local'}.${name.value}` + const projectName = fullName.value const mainModule = `${projectName}.Main` const entryPoint = { module: mainModule, definedOnType: mainModule, name: 'main' } return new ExecutionContext(lsRpcConnection, { @@ -509,6 +528,7 @@ export const useProjectStore = defineStore('project', () => { name: projectName, executionContext, module, + modulePath, contentRoots, awareness, computedValueRegistry,