Skip to content

Commit 8490589

Browse files
version navigation (#231)
1 parent ab28fcb commit 8490589

File tree

13 files changed

+222
-19
lines changed

13 files changed

+222
-19
lines changed

.github/workflows/build_and_publish.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,7 @@ jobs:
1515
- name: Install Node.js
1616
uses: actions/setup-node@v1
1717
with:
18-
node-version: 14.x
18+
node-version: 20
1919
- run: npm install
2020
- name: Run tests
2121
run: npm test

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,11 @@
11
# Change Log
22

3+
## [1.7.17] 2024-10-30
4+
5+
### Added
6+
7+
- previous/next versions in diff
8+
39
## [1.7.16] 2024-10-29
410

511
### Added

client/src/commands/registry.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -46,6 +46,10 @@ export const AbapFsCommands = {
4646
opendiff: "abapfs.opendiff",
4747
opendiffNormalized: "abapfs.opendiffNormalized",
4848
togglediffNormalize: "abapfs.togglediffNormalize",
49+
prevRevLeft: "abapfs.prevRevLeft",
50+
nextRevLeft: "abapfs.nextRevLeft",
51+
prevRevRight: "abapfs.prevRevRight",
52+
nextRevRight: "abapfs.nextRevRight",
4953
changequickdiff: "abapfs.changequickdiff",
5054
remotediff: "abapfs.remotediff",
5155
comparediff: "abapfs.comparediff",

client/src/context.ts

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
import { commands } from "vscode"
2+
3+
export type AbapFsContexts = "abapfs:showActivate" |
4+
"abapfs:atc:autorefreshOn" |
5+
"abapfs:atc:exemptFilterOn" |
6+
"abapfs:atcdoc:navigation:back" |
7+
"abapfs:atcdoc:navigation:next" |
8+
"abapfs:extensionActive" |
9+
"abapfs:extensionActive" |
10+
"abapfs:showTableContentIcon" |
11+
"abapfs:enableLeftPrevRev" |
12+
"abapfs:enableLeftNextRev" |
13+
"abapfs:enableRightPrevRev" |
14+
"abapfs:enableRightNextRev"
15+
16+
export const setContext = (key: AbapFsContexts, value: unknown) => commands.executeCommand("setContext", key, value)

client/src/extension.ts

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,6 @@ import { atcProvider, registerSCIDecorator } from "./views/abaptestcockpit"
44
import { FsProvider } from "./fs/FsProvider"
55
import {
66
window,
7-
commands,
87
workspace,
98
ExtensionContext,
109
languages
@@ -33,6 +32,7 @@ import { dumpProvider } from "./views/dumps/dumps"
3332
import { registerAbapDebugger } from "./adt/debugger"
3433
import { ATCDocumentation } from "./views/abaptestcockpit/documentation"
3534
import { tracesProvider } from "./views/traces"
35+
import { setContext } from "./context"
3636

3737
export let context: ExtensionContext
3838

@@ -102,7 +102,7 @@ export async function activate(ctx: ExtensionContext): Promise<AbapFsApi> {
102102

103103
LanguageCommands.start(context)
104104

105-
commands.executeCommand("setContext", "abapfs:extensionActive", true)
105+
setContext("abapfs:extensionActive", true)
106106
restoreLocks()
107107
registerAbapGit(context)
108108

@@ -123,6 +123,6 @@ export async function deactivate() {
123123
window.showInformationMessage(
124124
"Locks will be dropped now. If the relevant editors are still open they will be restored later"
125125
)
126-
commands.executeCommand("setContext", "abapfs:extensionActive", false)
126+
setContext("abapfs:extensionActive", false)
127127
return disconnect()
128128
}

client/src/lib/vscodefunctions.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,8 @@ import {
1010
Position,
1111
Range,
1212
OpenDialogOptions,
13-
QuickPickOptions
13+
QuickPickOptions,
14+
commands
1415
} from "vscode"
1516
import { splitAdtUriInternal, isUnDefined, isFn, isNonNullable, caughtToString, isString } from "./functions"
1617
import { Range as ApiRange, UriParts } from "abap-adt-api"

client/src/listeners.ts

Lines changed: 38 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@ import {
88
Disposable,
99
Event,
1010
TextDocumentWillSaveEvent,
11-
workspace
11+
workspace,
12+
TabInputTextDiff
1213
} from "vscode"
1314

1415
import { caughtToString, debounce, log, viewableObjecttypes } from "./lib"
@@ -17,8 +18,9 @@ import { AbapObject } from "abapobject"
1718
import { isAbapStat } from "abapfs"
1819
import { isCsrfError } from "abap-adt-api"
1920
import { LockStatus } from "abapfs/out/lockObject"
20-
import { IncludeProvider } from "./adt/includes"
2121
import { uriAbapFile } from "./adt/operations/AdtObjectFinder"
22+
import { versionRevisions } from "./scm/abaprevisions"
23+
import { setContext } from "./context"
2224

2325
export const listenersubscribers: ((...x: any[]) => Disposable)[] = []
2426

@@ -151,7 +153,7 @@ function isInactive(obj: AbapObject): boolean {
151153
function showHidedbIcon(editor?: TextEditor) {
152154
try {
153155
const type = uriAbapFile(editor?.document.uri)?.object.type
154-
commands.executeCommand("setContext", "abapfs:showTableContentIcon", viewableObjecttypes.has(type))
156+
setContext("abapfs:showTableContentIcon", viewableObjecttypes.has(type))
155157
} catch (error) { }
156158
}
157159

@@ -175,7 +177,7 @@ export async function showHideActivate(editor?: TextEditor, refresh = false) {
175177
}
176178
// race condition, active editor might have changed while async operation was pending
177179
if (editor !== window.activeTextEditor) return
178-
await commands.executeCommand("setContext", "abapfs:showActivate", shouldShow)
180+
await setContext("abapfs:showActivate", shouldShow)
179181
}
180182
export async function activationStateListener(uri: Uri) {
181183
const editor = window.activeTextEditor
@@ -185,11 +187,42 @@ export async function activationStateListener(uri: Uri) {
185187
await showHideActivate(editor)
186188
}
187189
}
188-
190+
const setRevisionContext = (leftprev: boolean, leftnext: boolean, rightprev: boolean, rightnext: boolean) => {
191+
setContext("abapfs:enableLeftNextRev", leftnext)
192+
setContext("abapfs:enableLeftPrevRev", leftprev)
193+
setContext("abapfs:enableRightNextRev", rightnext)
194+
setContext("abapfs:enableRightPrevRev", rightprev)
195+
}
196+
const enableRevNavigation = async (editor: TextEditor | undefined) => {
197+
if (editor) {
198+
const firstlast = async (u: Uri): Promise<[boolean, boolean]> => {
199+
const v = await versionRevisions(u)
200+
if (!v) return [false, false]
201+
const { revision, revisions } = v
202+
const idx = revisions.findIndex(r => r.uri === revision.uri)
203+
const hasNext = idx > 0
204+
const hasprev = idx >= 0 && idx < revisions.length - 1
205+
return [hasprev, hasNext]
206+
}
207+
try {
208+
const tab = window.tabGroups.activeTabGroup.activeTab
209+
if (tab?.input instanceof TabInputTextDiff) {
210+
const { original, modified } = tab.input
211+
const lefts = await firstlast(original)
212+
const rights = await firstlast(modified)
213+
if (rights && lefts) return setRevisionContext(...lefts, ...rights)
214+
}
215+
} catch (error) {
216+
// on error just disable all
217+
}
218+
}
219+
return setRevisionContext(false, false, false, false)
220+
}
189221
export async function activeTextEditorChangedListener(
190222
editor: TextEditor | undefined
191223
) {
192224
showHidedbIcon(editor)
225+
enableRevNavigation(editor)
193226
try {
194227
if (editor && editor.document.uri.scheme === ADTSCHEME) {
195228
await showHideActivate(editor)

client/src/scm/abaprevisions/commands.ts

Lines changed: 64 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@ import { abapUri, uriRoot, getOrCreateRoot, getClient, ADTSCHEME, rootIsConnecte
44
import { AbapRevisionService, revLabel } from "./abaprevisionservice"
55
import { ADTClient, Revision } from "abap-adt-api"
66
import { AbapQuickDiff } from "./quickdiff"
7-
import { revisionUri } from "./documentprovider"
7+
import { decodeRevisioUrl, revisionUri } from "./documentprovider"
88
import { RemoteManager, formatKey } from "../../config"
99
import { isAbapFile } from "abapfs"
1010
import { AGroup, AState } from "./abapscm"
@@ -47,6 +47,31 @@ const loadRevisions = async (uri: Uri, withRefresh = true) => {
4747
const revisions = await service.uriRevisions(uri, withRefresh)
4848
return revisions || []
4949
}
50+
interface UriRevisions { uri: Uri, normalized: boolean, revision: Revision, revisions: Revision[] }
51+
export const versionRevisions = async (v: Uri, refresh = false): Promise<UriRevisions | undefined> => {
52+
const decoded = decodeRevisioUrl(v)
53+
if (decoded) {
54+
const { uri, revision, normalized } = decoded
55+
const revisions = await loadRevisions(uri, refresh)
56+
const found = revisions.find(r => r.uri === revision.uri)
57+
if (found) return { uri, revision, revisions, normalized }
58+
}
59+
}
60+
61+
const currentRevisions = async () => {
62+
const tab = window.tabGroups.activeTabGroup.activeTab
63+
if (tab?.input instanceof TabInputTextDiff) {
64+
const { original, modified } = tab.input
65+
const lefts = await versionRevisions(original)
66+
const leftindex = lefts?.revisions.findIndex(r => r.uri === lefts.revision.uri) ?? -1
67+
const rights = leftindex >= 0 ? await versionRevisions(modified) : undefined
68+
const rightindex = rights?.revisions.findIndex(r => r.uri === rights.revision.uri) ?? -1
69+
if (!lefts || !rights || rightindex < 0 || leftindex < 0) return
70+
71+
return { lefts, rights, leftindex, rightindex }
72+
}
73+
74+
}
5075

5176
const pickRevision = async (revisions: Revision[], title = "Select version") => {
5277
if (!revisions.length) return
@@ -161,10 +186,47 @@ export class AbapRevisionCommands {
161186
if (!abapUri(uri)) return
162187
const leftRev = await selectRevision(uri, "Select version for left pane")
163188
if (!leftRev) return
164-
const rightRev = await selectRevision(uri, "Select version for right pane")
189+
const rightRev = await selectRevision(uri, "Select version for right pane", false)
165190
if (!rightRev) return
166191
displayRevDiff(rightRev, leftRev, uri)
167192
}
193+
@command(AbapFsCommands.prevRevLeft)
194+
private async prevLeft() {
195+
const current = await currentRevisions()
196+
if (current) {
197+
const left = current.lefts.revisions[current.leftindex + 1]
198+
if (left) return displayRevDiff(current.rights.revision, left, current.lefts.uri, current.lefts.normalized)
199+
}
200+
return window.showWarningMessage("Failed to determine version")
201+
}
202+
@command(AbapFsCommands.prevRevRight)
203+
private async prevRight() {
204+
const current = await currentRevisions()
205+
if (current) {
206+
const right = current.rights.revisions[current.rightindex + 1]
207+
if (right) return displayRevDiff(right, current.lefts.revision, current.lefts.uri, current.lefts.normalized)
208+
}
209+
return window.showWarningMessage("Failed to determine version")
210+
}
211+
@command(AbapFsCommands.nextRevLeft)
212+
private async nextLeft() {
213+
const current = await currentRevisions()
214+
if (current) {
215+
const left = current.lefts.revisions[current.leftindex - 1]
216+
if (left) return displayRevDiff(current.rights.revision, left, current.lefts.uri, current.lefts.normalized)
217+
}
218+
return window.showWarningMessage("Failed to determine version")
219+
220+
}
221+
@command(AbapFsCommands.nextRevRight)
222+
private async nextRight() {
223+
const current = await currentRevisions()
224+
if (current) {
225+
const right = current.rights.revisions[current.rightindex - 1]
226+
if (right) return displayRevDiff(right, current.lefts.revision, current.lefts.uri, current.lefts.normalized)
227+
}
228+
return window.showWarningMessage("Failed to determine version")
229+
}
168230

169231
private static async showMerge(uri: Uri, incomingUri: Uri, incomings: Revision[], locals: Revision[], incoming: Revision, conflict: Revision) {
170232
const baseVer = await pickCommonAncestor(locals, conflict, incomings, incoming)

client/src/scm/abaprevisions/documentprovider.ts

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,12 @@ type Selector = SimpleSelector | QuickDiffSelector
2323
const isQuickDiff = (s: Selector): s is QuickDiffSelector =>
2424
s.type === "quickdiff"
2525

26+
interface RevisionDetails {
27+
uri: Uri,
28+
revision: Revision,
29+
normalized: boolean
30+
}
31+
2632
export function revisionUri(uri: Uri, revision?: Revision, normalized = false) {
2733
if (!abapUri(uri)) return
2834
const selector: SimpleSelector = {
@@ -35,6 +41,18 @@ export function revisionUri(uri: Uri, revision?: Revision, normalized = false) {
3541
return uri.with({ scheme: ADTREVISIONSCHEME, fragment })
3642
}
3743

44+
export const decodeRevisioUrl = (uri: Uri): RevisionDetails | undefined => {
45+
if (uri.scheme !== ADTREVISIONSCHEME) return
46+
try {
47+
const selector: Selector = JSON.parse(atob(uri.fragment))
48+
if (isQuickDiff(selector) || !selector.revision) return
49+
const { revision, normalized } = selector
50+
return { uri: uri.with({ scheme: ADTSCHEME, fragment: selector.origFragment }), revision, normalized }
51+
} catch (error) {
52+
return
53+
}
54+
}
55+
3856
export const quickDiffUri = (uri: Uri) => {
3957
if (!abapUri(uri)) return
4058
const selector: Selector = {

client/src/scm/abaprevisions/index.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
11
export { registerRevisionModel } from "./abapscm"
22
export { AbapRevisionLens } from "./lenses"
33
export { AbapScm } from "./abapscm"
4-
export { displayRevDiff } from "./commands"
4+
export { displayRevDiff, versionRevisions } from "./commands"

0 commit comments

Comments
 (0)