Skip to content

Commit

Permalink
Merge branch 'develop' into wip/radeusgd/9048-resolve-enso-uri
Browse files Browse the repository at this point in the history
  • Loading branch information
jdunkerley authored Mar 1, 2024
2 parents f4944e1 + fad1b45 commit eca888b
Show file tree
Hide file tree
Showing 30 changed files with 769 additions and 243 deletions.
2 changes: 2 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -621,6 +621,7 @@
- [Adjusted expression handling and new `Simple_Expression` type.][9128]
- [Allow reading Data Links configured locally or in the Cloud.][9215]
- [Allow using `enso://` paths in `Data.read` and other places.][9225]
- [Update the XML methods and add more capabilities to document.][9233]

[debug-shortcuts]:
https://github.com/enso-org/enso/blob/develop/app/gui/docs/product/shortcuts.md#debug
Expand Down Expand Up @@ -898,6 +899,7 @@
[9128]: https://github.com/enso-org/enso/pull/9128
[9215]: https://github.com/enso-org/enso/pull/9215
[9225]: https://github.com/enso-org/enso/pull/9225
[9233]: https://github.com/enso-org/enso/pull/9233

#### Enso Compiler

Expand Down
43 changes: 43 additions & 0 deletions app/gui2/shared/ast/parse.ts
Original file line number Diff line number Diff line change
Expand Up @@ -516,6 +516,49 @@ export function printBlock(
return code
}

/** @internal Use `Ast.code()' to stringify. */
export function printDocumented(
documented: Documented,
info: SpanMap,
offset: number,
parentIndent: string | undefined,
verbatim?: boolean,
): string {
const open = documented.fields.get('open')
const topIndent = parentIndent ?? open.whitespace ?? ''
let code = ''
code += open.node.code_
const minWhitespaceLength = topIndent.length + 1
let preferredWhitespace = topIndent + ' '
documented.fields.get('elements').forEach(({ token }, i) => {
if (i === 0) {
const whitespace = token.whitespace ?? ' '
code += whitespace
code += token.node.code_
preferredWhitespace += whitespace
} else if (token.node.tokenType_ === RawAst.Token.Type.TextSection) {
if (token.whitespace && (verbatim || token.whitespace.length >= minWhitespaceLength))
code += token.whitespace
else code += preferredWhitespace
code += token.node.code_
} else {
code += token.whitespace ?? ''
code += token.node.code_
}
})
code += documented.fields
.get('newlines')
.map(({ whitespace, node }) => (whitespace ?? '') + node.code_)
.join('')
if (documented.expression) {
code += documented.fields.get('expression')?.whitespace ?? topIndent
code += documented.expression.printSubtree(info, offset + code.length, topIndent, verbatim)
}
const span = nodeKey(offset, code.length)
map.setIfUndefined(info.nodes, span, (): Ast[] => []).unshift(documented)
return code
}

/** Parse the input as a block. */
export function parseBlock(code: string, inModule?: MutableModule): Owned<MutableBodyBlock> {
return parseBlockWithSpans(code, inModule).root
Expand Down
57 changes: 51 additions & 6 deletions app/gui2/shared/ast/tree.ts
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,7 @@ import {
print,
printAst,
printBlock,
printDocumented,
syncToCode,
} from './parse'

Expand Down Expand Up @@ -1366,7 +1367,7 @@ export interface MutableTextLiteral extends TextLiteral, MutableAst {}
applyMixins(MutableTextLiteral, [MutableAst])

interface DocumentedFields {
open: NodeChild<SyncTokenId> | undefined
open: NodeChild<SyncTokenId>
elements: TextToken[]
newlines: NodeChild<SyncTokenId>[]
expression: NodeChild<AstId> | undefined
Expand All @@ -1382,19 +1383,29 @@ export class Documented extends Ast {
if (parsed instanceof MutableDocumented) return parsed
}

static new(text: string, expression: Owned) {
return this.concrete(
expression.module,
undefined,
textToUninterpolatedElements(text),
undefined,
autospaced(expression),
)
}

static concrete(
module: MutableModule,
open: NodeChild<Token> | undefined,
elements: TextToken<OwnedRefs>[],
newlines: NodeChild<Token>[],
newlines: NodeChild<Token>[] | undefined,
expression: NodeChild<Owned> | undefined,
) {
const base = module.baseObject('Documented')
const id_ = base.get('id')
const fields = composeFieldData(base, {
open,
open: open ?? unspaced(Token.new('##', RawAst.Token.Type.Operator)),
elements: elements.map((e) => mapRefs(e, ownedToRaw(module, id_))),
newlines,
newlines: newlines ?? [unspaced(Token.new('\n', RawAst.Token.Type.Newline))],
expression: concreteChild(module, expression, id_),
})
return asOwned(new MutableDocumented(module, fields))
Expand All @@ -1412,16 +1423,34 @@ export class Documented extends Ast {

*concreteChildren(_verbatim?: boolean): IterableIterator<RawNodeChild> {
const { open, elements, newlines, expression } = getAll(this.fields)
if (open) yield open
for (const e of elements) yield* fieldConcreteChildren(e)
yield open
for (const { token } of elements) yield token
yield* newlines
if (expression) yield expression
}

printSubtree(
info: SpanMap,
offset: number,
parentIndent: string | undefined,
verbatim?: boolean,
): string {
return printDocumented(this, info, offset, parentIndent, verbatim)
}
}
export class MutableDocumented extends Documented implements MutableAst {
declare readonly module: MutableModule
declare readonly fields: FixedMap<AstFields & DocumentedFields>

setDocumentationText(text: string) {
this.fields.set(
'elements',
textToUninterpolatedElements(text).map((owned) =>
mapRefs(owned, ownedToRaw(this.module, this.id)),
),
)
}

setExpression<T extends MutableAst>(value: Owned<T> | undefined) {
this.fields.set('expression', unspaced(this.claimChild(value)))
}
Expand All @@ -1431,6 +1460,22 @@ export interface MutableDocumented extends Documented, MutableAst {
}
applyMixins(MutableDocumented, [MutableAst])

function textToUninterpolatedElements(text: string): TextToken<OwnedRefs>[] {
const elements = new Array<TextToken<OwnedRefs>>()
text.split('\n').forEach((line, i) => {
if (i)
elements.push({
type: 'token',
token: unspaced(Token.new('\n', RawAst.Token.Type.TextNewline)),
})
elements.push({
type: 'token',
token: autospaced(Token.new(line, RawAst.Token.Type.TextSection)),
})
})
return elements
}

interface InvalidFields {
expression: NodeChild<AstId>
}
Expand Down
1 change: 0 additions & 1 deletion app/gui2/src/bindings.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export const componentBrowserBindings = defineKeybinds('component-browser', {
applySuggestion: ['Tab'],
acceptSuggestion: ['Enter'],
acceptInput: ['Mod+Enter'],
cancelEditing: ['Escape'],
moveUp: ['ArrowUp'],
moveDown: ['ArrowDown'],
})
Expand Down
6 changes: 6 additions & 0 deletions app/gui2/src/components/CircularMenu.vue
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ const emit = defineEmits<{
'update:isDocsVisible': [isDocsVisible: boolean]
'update:isVisualizationVisible': [isVisualizationVisible: boolean]
startEditing: []
startEditingComment: []
}>()
</script>

Expand All @@ -24,6 +25,11 @@ const emit = defineEmits<{
@pointerup.stop
@click.stop
>
<SvgIcon
name="comment"
class="icon-container button slot2"
@click.stop="emit('startEditingComment')"
/>
<ToggleIcon
icon="eye"
class="icon-container button slot5"
Expand Down
8 changes: 4 additions & 4 deletions app/gui2/src/components/CodeEditor.vue
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<script setup lang="ts">
import type { ChangeSet, Diagnostic, Highlighter } from '@/components/CodeEditor/codemirror'
import { Annotation, StateEffect, StateField } from '@/components/CodeEditor/codemirror'
import { usePointer } from '@/composables/events'
import { useGraphStore, type NodeId } from '@/stores/graph'
import { useProjectStore } from '@/stores/project'
Expand All @@ -18,6 +17,9 @@ import { computed, onMounted, onUnmounted, ref, shallowRef, watch, watchEffect }
// Use dynamic imports to aid code splitting. The codemirror dependency is quite large.
const {
Annotation,
StateEffect,
StateField,
bracketMatching,
foldGutter,
lintGutter,
Expand All @@ -33,6 +35,7 @@ const {
forceLinting,
lsDiagnosticsToCMDiagnostics,
hoverTooltip,
textEditToChangeSpec,
} = await import('@/components/CodeEditor/codemirror')
const projectStore = useProjectStore()
Expand Down Expand Up @@ -185,9 +188,6 @@ function changeSetToTextEdits(changes: ChangeSet) {
)
return textEdits
}
function textEditToChangeSpec({ range: [from, to], insert }: SourceRangeEdit) {
return { from, to, insert }
}
let pendingChanges: ChangeSet | undefined
let currentModule: MutableModule | undefined
Expand Down
6 changes: 6 additions & 0 deletions app/gui2/src/components/CodeEditor/codemirror.ts
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ import {
syntaxTree,
} from '@codemirror/language'
import { type Diagnostic } from '@codemirror/lint'
import type { ChangeSpec } from '@codemirror/state'
import { hoverTooltip as originalHoverTooltip, type TooltipView } from '@codemirror/view'
import {
NodeProp,
Expand All @@ -43,6 +44,7 @@ import { styleTags, tags } from '@lezer/highlight'
import { EditorView } from 'codemirror'
import type { Diagnostic as LSDiagnostic } from 'shared/languageServerTypes'
import { tryGetSoleValue } from 'shared/util/data/iterable'
import type { SourceRangeEdit } from 'shared/util/data/text'

export function lsDiagnosticsToCMDiagnostics(
source: string,
Expand Down Expand Up @@ -193,3 +195,7 @@ export function hoverTooltip(
}
})
}

export function textEditToChangeSpec({ range: [from, to], insert }: SourceRangeEdit): ChangeSpec {
return { from, to, insert }
}
41 changes: 21 additions & 20 deletions app/gui2/src/components/ComponentBrowser.vue
Original file line number Diff line number Diff line change
Expand Up @@ -11,12 +11,14 @@ import ToggleIcon from '@/components/ToggleIcon.vue'
import { useApproach } from '@/composables/animation'
import { useEvent, useResizeObserver } from '@/composables/events'
import type { useNavigator } from '@/composables/navigator'
import { injectInteractionHandler, type Interaction } from '@/providers/interactionHandler'
import { useGraphStore } from '@/stores/graph'
import type { RequiredImport } from '@/stores/graph/imports'
import { useProjectStore } from '@/stores/project'
import { groupColorStyle, useSuggestionDbStore } from '@/stores/suggestionDatabase'
import { SuggestionKind, type SuggestionEntry } from '@/stores/suggestionDatabase/entry'
import type { VisualizationDataSource } from '@/stores/visualization'
import { targetIsOutside } from '@/util/autoBlur'
import { tryGetIndex } from '@/util/data/array'
import type { Opt } from '@/util/data/opt'
import { allRanges } from '@/util/data/range'
Expand All @@ -35,6 +37,7 @@ const COMPONENT_BROWSER_TO_NODE_OFFSET = new Vec2(-4, -4)
const projectStore = useProjectStore()
const suggestionDbStore = useSuggestionDbStore()
const graphStore = useGraphStore()
const interaction = injectInteractionHandler()
const props = defineProps<{
nodePosition: Vec2
Expand All @@ -47,7 +50,24 @@ const emit = defineEmits<{
canceled: []
}>()
const cbOpen: Interaction = {
cancel: () => {
emit('canceled')
},
click: (e: PointerEvent) => {
if (targetIsOutside(e, cbRoot)) {
if (input.anyChange.value) {
acceptInput()
} else {
interaction.cancel(cbOpen)
}
}
return false
},
}
onMounted(() => {
interaction.setCurrent(cbOpen)
input.reset(props.usage)
if (inputField.value != null) {
inputField.value.focus({ preventScroll: true })
Expand Down Expand Up @@ -159,23 +179,6 @@ function preventNonInputDefault(e: Event) {
}
}
useEvent(
window,
'pointerdown',
(event) => {
if (event.button !== 0) return
if (!(event.target instanceof Element)) return
if (!cbRoot.value?.contains(event.target)) {
if (input.anyChange.value) {
emit('accepted', input.code.value, input.importsToAdd())
} else {
emit('canceled')
}
}
},
{ capture: true },
)
const inputElement = ref<HTMLElement>()
const inputSize = useResizeObserver(inputElement, false)
Expand Down Expand Up @@ -357,6 +360,7 @@ function acceptSuggestion(index: Opt<Component> = null) {
function acceptInput() {
emit('accepted', input.code.value.trim(), input.importsToAdd())
interaction.end(cbOpen)
}
// === Key Events Handler ===
Expand Down Expand Up @@ -386,9 +390,6 @@ const handler = componentBrowserBindings.handler({
}
scrolling.scrollWithTransition({ type: 'selected' })
},
cancelEditing() {
emit('canceled')
},
})
</script>

Expand Down
Loading

0 comments on commit eca888b

Please sign in to comment.